MIFOSX-1786 Allow usage of UGD (Templates) for Hooks
This commit is contained in:
+35
-29
@@ -13920,6 +13920,7 @@ Request Body:
|
|||||||
<li> events - array - Determines what events the hook is triggered for.
|
<li> events - array - Determines what events the hook is triggered for.
|
||||||
</li>
|
</li>
|
||||||
<li> config - hash - Required. Key/value pairs to provide settings for this hook. These settings vary between the templates.</li>
|
<li> config - hash - Required. Key/value pairs to provide settings for this hook. These settings vary between the templates.</li>
|
||||||
|
<li> templateId - Optional. The UGD template ID associated with the same entity (client or loan).</li>
|
||||||
<ul></p>
|
<ul></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="method-example">
|
<div class="method-example">
|
||||||
@@ -13934,6 +13935,7 @@ Request Body:
|
|||||||
"name": "Web",
|
"name": "Web",
|
||||||
"isActive": true,
|
"isActive": true,
|
||||||
"displayName": "Kremlin",
|
"displayName": "Kremlin",
|
||||||
|
"templateId": 1,
|
||||||
"events": [
|
"events": [
|
||||||
{
|
{
|
||||||
"actionName": "DISBURSE",
|
"actionName": "DISBURSE",
|
||||||
@@ -13980,6 +13982,8 @@ GET https://DomainName/api/v1/hooks
|
|||||||
"isActive": true,
|
"isActive": true,
|
||||||
"createdAt": [2014, 9, 16],
|
"createdAt": [2014, 9, 16],
|
||||||
"updatedAt": [2014, 9, 16],
|
"updatedAt": [2014, 9, 16],
|
||||||
|
"templateId": 1,
|
||||||
|
"templateName": "My UGD",
|
||||||
"events": [
|
"events": [
|
||||||
{
|
{
|
||||||
"actionName": "DISBURSE",
|
"actionName": "DISBURSE",
|
||||||
@@ -14027,6 +14031,8 @@ GET https://DomainName/api/v1/hooks/{hookId}
|
|||||||
"isActive": true,
|
"isActive": true,
|
||||||
"createdAt": [2014, 9, 16],
|
"createdAt": [2014, 9, 16],
|
||||||
"updatedAt": [2014, 9, 16],
|
"updatedAt": [2014, 9, 16],
|
||||||
|
"templateId": 1,
|
||||||
|
"templateName": "My UGD",
|
||||||
"events": [
|
"events": [
|
||||||
{
|
{
|
||||||
"actionName": "DISBURSE",
|
"actionName": "DISBURSE",
|
||||||
@@ -33174,7 +33180,7 @@ No Request Body:
|
|||||||
|
|
||||||
<p>User Generated Documents (and other types of templates) can aggregate data from several Mifos X back-end API calls via <b>mappers</b>. Mappers can even access non-Mifos X REST services from other servers. UGDs can render such data in tables, show images, etc. <i>TBD: Please have a look at some of the Example UGDs included in Mifos X (or <a href="https://mifosforge.jira.com/wiki/display/RES/UGD_FinalDoc">the Wiki page</a>, for now.).</i></p>
|
<p>User Generated Documents (and other types of templates) can aggregate data from several Mifos X back-end API calls via <b>mappers</b>. Mappers can even access non-Mifos X REST services from other servers. UGDs can render such data in tables, show images, etc. <i>TBD: Please have a look at some of the Example UGDs included in Mifos X (or <a href="https://mifosforge.jira.com/wiki/display/RES/UGD_FinalDoc">the Wiki page</a>, for now.).</i></p>
|
||||||
|
|
||||||
<p>UGDs can be assigned to an entity like <b>client</b> or <b>loan</b> and be of a typ like <b>document</b>. The entity and type of a UGD is only there for the convenience of user agents (UIs), in order to know where to show UGDs for the user (i.e. which tab). The Template Engine back-end runner does not actually need this metadata.</p>
|
<p>UGDs can be assigned to an entity like <b>client</b> or <b>loan</b> and be of a type like <b>Document</b> or <b>SMS</b>. The entity and type of a UGD is only there for the convenience of user agents (UIs), in order to know where to show UGDs for the user (i.e. which tab). The Template Engine back-end runner does not actually need this metadata.</p>
|
||||||
<table class=matrixHeading>
|
<table class=matrixHeading>
|
||||||
<tr class="matrixHeadingBG">
|
<tr class="matrixHeadingBG">
|
||||||
<td><div class="mifosXHeading2">Field Descriptions</div></td>
|
<td><div class="mifosXHeading2">Field Descriptions</div></td>
|
||||||
@@ -33191,8 +33197,8 @@ No Request Body:
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class=fielddesc>
|
<td class=fielddesc>
|
||||||
For now only the type document is supported.
|
For now only the types Document and SMS are supported.
|
||||||
In a next version UGDs may be created for e-mails oder SMS.
|
In a next version, UGDs may be created for e-mails as well.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class=alt>
|
<tr class=alt>
|
||||||
@@ -33271,14 +33277,14 @@ GET https://DomainName/api/v1/templates/template
|
|||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "Test",
|
"name": "Test",
|
||||||
"entity": "loan",
|
"entity": 1,
|
||||||
"type": "Document",
|
"type": 0,
|
||||||
"text": "This is a loan for {{loan.clientName}}",
|
"text": "This is a loan for {{loan.clientName}}",
|
||||||
"mappers": [
|
"mappers": [
|
||||||
{
|
{
|
||||||
"mapperorder": 0,
|
"mappersorder": 0,
|
||||||
"mapperkey": "loan",
|
"mapperskey": "loan",
|
||||||
"mappervalue": "loans/{{loanId}}?associations=all&tenantIdentifier={{tenantIdentifier}}",
|
"mappersvalue": "loans/{{loanId}}?associations=all&tenantIdentifier={{tenantIdentifier}}",
|
||||||
"id": 1
|
"id": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -33315,14 +33321,14 @@ Request Body:
|
|||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "Test",
|
"name": "Test",
|
||||||
"entity": "loan",
|
"entity": 1,
|
||||||
"type": "Document",
|
"type": 0,
|
||||||
"text": "This is a loan for {{loan.clientName}}",
|
"text": "This is a loan for {{loan.clientName}}",
|
||||||
"mappers": [
|
"mappers": [
|
||||||
{
|
{
|
||||||
"mapperorder": 0,
|
"mappersorder": 0,
|
||||||
"mapperkey": "loan",
|
"mapperskey": "loan",
|
||||||
"mappervalue": "loans/{{loanId}}?associations=all&tenantIdentifier={{tenantIdentifier}}",
|
"mappersvalue": "loans/{{loanId}}?associations=all&tenantIdentifier={{tenantIdentifier}}",
|
||||||
"id": 1
|
"id": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -33372,7 +33378,7 @@ Request Body:
|
|||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>SMS (not yet)</td>
|
<td>SMS</td>
|
||||||
<td>2</td>
|
<td>2</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@@ -33386,14 +33392,14 @@ GET https://DomainName/api/v1/templates
|
|||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "Test",
|
"name": "Test",
|
||||||
"entity": "loan",
|
"entity": 1,
|
||||||
"type": "Document",
|
"type": 0,
|
||||||
"text": "This is a loan for {{loan.clientName}}",
|
"text": "This is a loan for {{loan.clientName}}",
|
||||||
"mappers": [
|
"mappers": [
|
||||||
{
|
{
|
||||||
"mapperorder": 0,
|
"mappersorder": 0,
|
||||||
"mapperkey": "loan",
|
"mapperskey": "loan",
|
||||||
"mappervalue": "loans/{{loanId}}?associations=all&tenantIdentifier={{tenantIdentifier}}",
|
"mappersvalue": "loans/{{loanId}}?associations=all&tenantIdentifier={{tenantIdentifier}}",
|
||||||
"id": 1
|
"id": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -33419,14 +33425,14 @@ GET https://DomainName/api/v1/templates/{Id}
|
|||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "Test",
|
"name": "Test",
|
||||||
"entity": "loan",
|
"entity": 1,
|
||||||
"type": "Document",
|
"type": 0,
|
||||||
"text": "This is a loan for {{loan.clientName}}",
|
"text": "This is a loan for {{loan.clientName}}",
|
||||||
"mappers": [
|
"mappers": [
|
||||||
{
|
{
|
||||||
"mapperorder": 0,
|
"mappersorder": 0,
|
||||||
"mapperkey": "loan",
|
"mapperskey": "loan",
|
||||||
"mappervalue": "loans/{{loanId}}?associations=all&tenantIdentifier={{tenantIdentifier}}",
|
"mappersvalue": "loans/{{loanId}}?associations=all&tenantIdentifier={{tenantIdentifier}}",
|
||||||
"id": 1
|
"id": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -33452,14 +33458,14 @@ Request Body:
|
|||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "Test",
|
"name": "Test",
|
||||||
"entity": "loan",
|
"entity": 1,
|
||||||
"type": "Document",
|
"type": 0,
|
||||||
"text": "This is a loan for {{loan.clientName}}",
|
"text": "This is a loan for {{loan.clientName}}",
|
||||||
"mappers": [
|
"mappers": [
|
||||||
{
|
{
|
||||||
"mapperorder": 0,
|
"mappersorder": 0,
|
||||||
"mapperkey": "loan",
|
"mapperskey": "loan",
|
||||||
"mappervalue": "loans/{{loanId}}?associations=all&tenantIdentifier={{tenantIdentifier}}",
|
"mappersvalue": "loans/{{loanId}}?associations=all&tenantIdentifier={{tenantIdentifier}}",
|
||||||
"id": 1
|
"id": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
+8
-2
@@ -45,12 +45,18 @@ public class HookApiConstants {
|
|||||||
|
|
||||||
public static final String actionNameParamName = "actionName";
|
public static final String actionNameParamName = "actionName";
|
||||||
|
|
||||||
|
public static final String templateIdParamName = "templateId";
|
||||||
|
|
||||||
|
public static final String templateNameParamName = "templateName";
|
||||||
|
|
||||||
public static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(
|
public static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(
|
||||||
Arrays.asList(nameParamName, displayNameParamName,
|
Arrays.asList(nameParamName, displayNameParamName,
|
||||||
isActiveParamName, configParamName, eventsParamName));
|
templateIdParamName, isActiveParamName, configParamName,
|
||||||
|
eventsParamName, templateNameParamName));
|
||||||
|
|
||||||
public static final Set<String> UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<>(
|
public static final Set<String> UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<>(
|
||||||
Arrays.asList(nameParamName, displayNameParamName,
|
Arrays.asList(nameParamName, displayNameParamName,
|
||||||
isActiveParamName, configParamName, eventsParamName));
|
templateIdParamName, isActiveParamName, configParamName,
|
||||||
|
eventsParamName, templateNameParamName));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-7
@@ -5,11 +5,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.infrastructure.hooks.data;
|
package org.mifosplatform.infrastructure.hooks.data;
|
||||||
|
|
||||||
|
import org.joda.time.LocalDate;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.joda.time.LocalDate;
|
|
||||||
|
|
||||||
public class HookData implements Serializable {
|
public class HookData implements Serializable {
|
||||||
|
|
||||||
private final Long id;
|
private final Long id;
|
||||||
@@ -18,6 +18,8 @@ public class HookData implements Serializable {
|
|||||||
private final Boolean isActive;
|
private final Boolean isActive;
|
||||||
private final LocalDate createdAt;
|
private final LocalDate createdAt;
|
||||||
private final LocalDate updatedAt;
|
private final LocalDate updatedAt;
|
||||||
|
private final Long templateId;
|
||||||
|
private final String templateName;
|
||||||
|
|
||||||
// associations
|
// associations
|
||||||
private final List<Event> events;
|
private final List<Event> events;
|
||||||
@@ -30,15 +32,17 @@ public class HookData implements Serializable {
|
|||||||
public static HookData instance(final Long id, final String name,
|
public static HookData instance(final Long id, final String name,
|
||||||
final String displayName, final boolean isActive,
|
final String displayName, final boolean isActive,
|
||||||
final LocalDate createdAt, final LocalDate updatedAt,
|
final LocalDate createdAt, final LocalDate updatedAt,
|
||||||
final List<Event> registeredEvents, final List<Field> config) {
|
final Long templateId, final List<Event> registeredEvents,
|
||||||
|
final List<Field> config, final String templateName) {
|
||||||
return new HookData(id, name, displayName, isActive, createdAt,
|
return new HookData(id, name, displayName, isActive, createdAt,
|
||||||
updatedAt, registeredEvents, config, null, null);
|
updatedAt, templateId, registeredEvents, config, templateName,
|
||||||
|
null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HookData template(final List<HookTemplateData> templates,
|
public static HookData template(final List<HookTemplateData> templates,
|
||||||
final List<Grouping> groupings) {
|
final List<Grouping> groupings) {
|
||||||
return new HookData(null, null, null, null, null, null, null, null,
|
return new HookData(null, null, null, null, null, null, null, null,
|
||||||
templates, groupings);
|
null, null, templates, groupings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HookData templateExisting(final HookData hookData,
|
public static HookData templateExisting(final HookData hookData,
|
||||||
@@ -46,13 +50,15 @@ public class HookData implements Serializable {
|
|||||||
final List<Grouping> groupings) {
|
final List<Grouping> groupings) {
|
||||||
return new HookData(hookData.id, hookData.name, hookData.displayName,
|
return new HookData(hookData.id, hookData.name, hookData.displayName,
|
||||||
hookData.isActive, hookData.createdAt, hookData.updatedAt,
|
hookData.isActive, hookData.createdAt, hookData.updatedAt,
|
||||||
hookData.events, hookData.config, templates, groupings);
|
hookData.templateId, hookData.events, hookData.config,
|
||||||
|
hookData.templateName, templates, groupings);
|
||||||
}
|
}
|
||||||
|
|
||||||
private HookData(final Long id, final String name,
|
private HookData(final Long id, final String name,
|
||||||
final String displayName, final Boolean isActive,
|
final String displayName, final Boolean isActive,
|
||||||
final LocalDate createdAt, final LocalDate updatedAt,
|
final LocalDate createdAt, final LocalDate updatedAt,
|
||||||
final List<Event> events, final List<Field> config,
|
final Long templateId, final List<Event> events,
|
||||||
|
final List<Field> config, final String templateName,
|
||||||
final List<HookTemplateData> templates,
|
final List<HookTemplateData> templates,
|
||||||
final List<Grouping> groupings) {
|
final List<Grouping> groupings) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@@ -61,6 +67,8 @@ public class HookData implements Serializable {
|
|||||||
this.isActive = isActive;
|
this.isActive = isActive;
|
||||||
this.createdAt = createdAt;
|
this.createdAt = createdAt;
|
||||||
this.updatedAt = updatedAt;
|
this.updatedAt = updatedAt;
|
||||||
|
this.templateId = templateId;
|
||||||
|
this.templateName = templateName;
|
||||||
|
|
||||||
// associations
|
// associations
|
||||||
this.events = events;
|
this.events = events;
|
||||||
|
|||||||
+39
-24
@@ -5,34 +5,24 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.infrastructure.hooks.domain;
|
package org.mifosplatform.infrastructure.hooks.domain;
|
||||||
|
|
||||||
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.configParamName;
|
import com.google.gson.JsonArray;
|
||||||
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.displayNameParamName;
|
import com.google.gson.JsonElement;
|
||||||
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.eventsParamName;
|
|
||||||
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.isActiveParamName;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.persistence.CascadeType;
|
|
||||||
import javax.persistence.Column;
|
|
||||||
import javax.persistence.Entity;
|
|
||||||
import javax.persistence.JoinColumn;
|
|
||||||
import javax.persistence.ManyToOne;
|
|
||||||
import javax.persistence.OneToMany;
|
|
||||||
import javax.persistence.Table;
|
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.hibernate.annotations.LazyCollection;
|
import org.hibernate.annotations.LazyCollection;
|
||||||
import org.hibernate.annotations.LazyCollectionOption;
|
import org.hibernate.annotations.LazyCollectionOption;
|
||||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom;
|
import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom;
|
||||||
|
import org.mifosplatform.template.domain.Template;
|
||||||
import org.mifosplatform.useradministration.domain.AppUser;
|
import org.mifosplatform.useradministration.domain.AppUser;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
import com.google.gson.JsonArray;
|
import javax.persistence.*;
|
||||||
import com.google.gson.JsonElement;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.*;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "m_hook")
|
@Table(name = "m_hook")
|
||||||
@@ -56,25 +46,30 @@ public class Hook extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
@JoinColumn(name = "template_id", referencedColumnName = "id", nullable = false)
|
@JoinColumn(name = "template_id", referencedColumnName = "id", nullable = false)
|
||||||
private HookTemplate template;
|
private HookTemplate template;
|
||||||
|
|
||||||
|
@ManyToOne(optional = true)
|
||||||
|
@JoinColumn(name = "ugd_template_id", referencedColumnName = "id", nullable = false)
|
||||||
|
private Template ugdTemplate;
|
||||||
|
|
||||||
protected Hook() {
|
protected Hook() {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Hook fromJson(final JsonCommand command,
|
public static Hook fromJson(final JsonCommand command,
|
||||||
final HookTemplate template, final Set<HookConfiguration> config,
|
final HookTemplate template, final Set<HookConfiguration> config,
|
||||||
final Set<HookResource> events) {
|
final Set<HookResource> events, final Template ugdTemplate) {
|
||||||
final String displayName = command
|
final String displayName = command
|
||||||
.stringValueOfParameterNamed(displayNameParamName);
|
.stringValueOfParameterNamed(displayNameParamName);
|
||||||
Boolean isActive = command
|
Boolean isActive = command
|
||||||
.booleanObjectValueOfParameterNamed(isActiveParamName);
|
.booleanObjectValueOfParameterNamed(isActiveParamName);
|
||||||
if (isActive == null)
|
if (isActive == null)
|
||||||
isActive = false;
|
isActive = false;
|
||||||
return new Hook(template, displayName, isActive, config, events);
|
return new Hook(template, displayName, isActive, config, events,
|
||||||
|
ugdTemplate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Hook(final HookTemplate template, final String displayName,
|
private Hook(final HookTemplate template, final String displayName,
|
||||||
final Boolean isActive, final Set<HookConfiguration> config,
|
final Boolean isActive, final Set<HookConfiguration> config,
|
||||||
final Set<HookResource> events) {
|
final Set<HookResource> events, final Template ugdTemplate) {
|
||||||
|
|
||||||
this.template = template;
|
this.template = template;
|
||||||
|
|
||||||
@@ -91,6 +86,7 @@ public class Hook extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
this.events = associateEventsWithThisHook(events);
|
this.events = associateEventsWithThisHook(events);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.ugdTemplate = ugdTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<HookConfiguration> associateConfigWithThisHook(
|
private Set<HookConfiguration> associateConfigWithThisHook(
|
||||||
@@ -113,13 +109,21 @@ public class Hook extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
return this.template;
|
return this.template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Template getUgdTemplate() {
|
||||||
|
return this.ugdTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getUgdTemplateId() {
|
||||||
|
return this.ugdTemplate.getId();
|
||||||
|
}
|
||||||
|
|
||||||
public Set<HookConfiguration> getHookConfig() {
|
public Set<HookConfiguration> getHookConfig() {
|
||||||
return this.config;
|
return this.config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> update(final JsonCommand command) {
|
public Map<String, Object> update(final JsonCommand command) {
|
||||||
|
|
||||||
final Map<String, Object> actualChanges = new LinkedHashMap<>(4);
|
final Map<String, Object> actualChanges = new LinkedHashMap<>(5);
|
||||||
|
|
||||||
if (command.isChangeInStringParameterNamed(displayNameParamName,
|
if (command.isChangeInStringParameterNamed(displayNameParamName,
|
||||||
this.name)) {
|
this.name)) {
|
||||||
@@ -137,6 +141,13 @@ public class Hook extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
this.isActive = newValue;
|
this.isActive = newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (command.isChangeInLongParameterNamed(templateIdParamName,
|
||||||
|
getUgdTemplateId())) {
|
||||||
|
final Long newValue = command
|
||||||
|
.longValueOfParameterNamed(templateIdParamName);
|
||||||
|
actualChanges.put(templateIdParamName, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
// events
|
// events
|
||||||
if (command.hasParameter(eventsParamName)) {
|
if (command.hasParameter(eventsParamName)) {
|
||||||
final JsonArray jsonArray = command
|
final JsonArray jsonArray = command
|
||||||
@@ -184,4 +195,8 @@ public class Hook extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateUgdTemplate(final Template ugdTemplate) {
|
||||||
|
this.ugdTemplate = ugdTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -5,8 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.infrastructure.hooks.listener;
|
package org.mifosplatform.infrastructure.hooks.listener;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenant;
|
import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenant;
|
||||||
import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil;
|
import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil;
|
||||||
import org.mifosplatform.infrastructure.hooks.domain.Hook;
|
import org.mifosplatform.infrastructure.hooks.domain.Hook;
|
||||||
@@ -20,6 +18,8 @@ import org.mifosplatform.useradministration.domain.AppUser;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class MifosHookListener implements HookListener {
|
public class MifosHookListener implements HookListener {
|
||||||
|
|
||||||
|
|||||||
+63
-11
@@ -5,30 +5,43 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.infrastructure.hooks.processor;
|
package org.mifosplatform.infrastructure.hooks.processor;
|
||||||
|
|
||||||
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.apiKeyName;
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import org.codehaus.jackson.map.ObjectMapper;
|
||||||
import org.mifosplatform.infrastructure.hooks.domain.Hook;
|
import org.mifosplatform.infrastructure.hooks.domain.Hook;
|
||||||
import org.mifosplatform.infrastructure.hooks.domain.HookConfiguration;
|
import org.mifosplatform.infrastructure.hooks.domain.HookConfiguration;
|
||||||
import org.mifosplatform.infrastructure.hooks.domain.HookConfigurationRepository;
|
import org.mifosplatform.infrastructure.hooks.domain.HookConfigurationRepository;
|
||||||
import org.mifosplatform.infrastructure.hooks.processor.data.SmsProviderData;
|
import org.mifosplatform.infrastructure.hooks.processor.data.SmsProviderData;
|
||||||
|
import org.mifosplatform.portfolio.client.domain.Client;
|
||||||
|
import org.mifosplatform.portfolio.client.domain.ClientRepository;
|
||||||
|
import org.mifosplatform.template.service.TemplateMergeService;
|
||||||
import org.mifosplatform.useradministration.domain.AppUser;
|
import org.mifosplatform.useradministration.domain.AppUser;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import retrofit.Callback;
|
import retrofit.Callback;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import java.io.IOException;
|
||||||
import com.google.gson.JsonParser;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.apiKeyName;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class TwilioHookProcessor implements HookProcessor {
|
public class TwilioHookProcessor implements HookProcessor {
|
||||||
|
|
||||||
private final HookConfigurationRepository hookConfigurationRepository;
|
private final HookConfigurationRepository hookConfigurationRepository;
|
||||||
|
private final TemplateMergeService templateMergeService;
|
||||||
|
private final ClientRepository clientRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public TwilioHookProcessor(
|
public TwilioHookProcessor(
|
||||||
final HookConfigurationRepository hookConfigurationRepository) {
|
final HookConfigurationRepository hookConfigurationRepository,
|
||||||
|
final TemplateMergeService templateMergeService,
|
||||||
|
final ClientRepository clientRepository) {
|
||||||
this.hookConfigurationRepository = hookConfigurationRepository;
|
this.hookConfigurationRepository = hookConfigurationRepository;
|
||||||
|
this.templateMergeService = templateMergeService;
|
||||||
|
this.clientRepository = clientRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -47,9 +60,9 @@ public class TwilioHookProcessor implements HookProcessor {
|
|||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private void sendRequest(final SmsProviderData smsProviderData,
|
private void sendRequest(final SmsProviderData smsProviderData,
|
||||||
final String payload, final String entityName,
|
final String payload, String entityName, String actionName,
|
||||||
final String actionName, final String tenantIdentifier,
|
final String tenantIdentifier, final String authToken,
|
||||||
final String authToken, final Hook hook) {
|
final Hook hook) {
|
||||||
|
|
||||||
final WebHookService service = ProcessorHelper
|
final WebHookService service = ProcessorHelper
|
||||||
.createWebHookService(smsProviderData.getUrl());
|
.createWebHookService(smsProviderData.getUrl());
|
||||||
@@ -72,12 +85,51 @@ public class TwilioHookProcessor implements HookProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (apiKey != null && !apiKey.equals("")) {
|
if (apiKey != null && !apiKey.equals("")) {
|
||||||
final JsonObject json = new JsonParser().parse(payload)
|
JsonObject json = null;
|
||||||
.getAsJsonObject();
|
if (hook.getUgdTemplate() != null) {
|
||||||
|
entityName = "sms";
|
||||||
|
actionName = "send";
|
||||||
|
json = processUgdTemplate(payload, hook, authToken);
|
||||||
|
if (json == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
json = new JsonParser().parse(payload).getAsJsonObject();
|
||||||
|
}
|
||||||
service.sendSmsBridgeRequest(entityName, actionName,
|
service.sendSmsBridgeRequest(entityName, actionName,
|
||||||
tenantIdentifier, apiKey, json, callback);
|
tenantIdentifier, apiKey, json, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JsonObject processUgdTemplate(final String payload,
|
||||||
|
final Hook hook, final String authToken) {
|
||||||
|
JsonObject json = null;
|
||||||
|
try {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final HashMap<String, Object> map = new ObjectMapper().readValue(
|
||||||
|
payload, HashMap.class);
|
||||||
|
map.put("BASE_URI", System.getProperty("baseUrl"));
|
||||||
|
if (map.containsKey("clientId")) {
|
||||||
|
final Long clientId = new Long(Integer.toString((int) map
|
||||||
|
.get("clientId")));
|
||||||
|
final Client client = this.clientRepository.findOne(clientId);
|
||||||
|
final String mobileNo = client.mobileNo();
|
||||||
|
if (mobileNo != null && !mobileNo.isEmpty()) {
|
||||||
|
this.templateMergeService.setAuthToken(authToken);
|
||||||
|
final String compiledMessage = this.templateMergeService
|
||||||
|
.compile(hook.getUgdTemplate(), map)
|
||||||
|
.replace("<p>", "").replace("</p>", "");
|
||||||
|
final Map<String, String> jsonMap = new HashMap<>();
|
||||||
|
jsonMap.put("mobileNo", mobileNo);
|
||||||
|
jsonMap.put("message", compiledMessage);
|
||||||
|
final String jsonString = new Gson().toJson(jsonMap);
|
||||||
|
json = new JsonParser().parse(jsonString).getAsJsonObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-11
@@ -5,25 +5,20 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.infrastructure.hooks.serialization;
|
package org.mifosplatform.infrastructure.hooks.serialization;
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
import com.google.gson.JsonElement;
|
||||||
import java.util.ArrayList;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||||
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||||
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
|
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
|
||||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||||
|
import org.mifosplatform.infrastructure.hooks.api.HookApiConstants;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.google.gson.JsonElement;
|
import java.lang.reflect.Type;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import java.util.*;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class HookCommandFromApiJsonDeserializer {
|
public class HookCommandFromApiJsonDeserializer {
|
||||||
@@ -32,7 +27,8 @@ public class HookCommandFromApiJsonDeserializer {
|
|||||||
* The parameters supported for this command.
|
* The parameters supported for this command.
|
||||||
*/
|
*/
|
||||||
private final Set<String> supportedParameters = new HashSet<>(
|
private final Set<String> supportedParameters = new HashSet<>(
|
||||||
Arrays.asList("name", "displayName", "isActive", "events", "config"));
|
Arrays.asList("name", "displayName", "isActive", "events",
|
||||||
|
"config", "templateId"));
|
||||||
private final FromJsonHelper fromApiJsonHelper;
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@@ -62,6 +58,15 @@ public class HookCommandFromApiJsonDeserializer {
|
|||||||
baseDataValidator.reset().parameter("name").value(name).notBlank()
|
baseDataValidator.reset().parameter("name").value(name).notBlank()
|
||||||
.notExceedingLengthOf(100);
|
.notExceedingLengthOf(100);
|
||||||
|
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(
|
||||||
|
HookApiConstants.templateIdParamName, element)) {
|
||||||
|
final Long templateId = this.fromApiJsonHelper.extractLongNamed(
|
||||||
|
HookApiConstants.templateIdParamName, element);
|
||||||
|
baseDataValidator.reset()
|
||||||
|
.parameter(HookApiConstants.templateIdParamName)
|
||||||
|
.value(templateId).notNull().integerGreaterThanZero();
|
||||||
|
}
|
||||||
|
|
||||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,6 +92,15 @@ public class HookCommandFromApiJsonDeserializer {
|
|||||||
.notExceedingLengthOf(100);
|
.notExceedingLengthOf(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(
|
||||||
|
HookApiConstants.templateIdParamName, element)) {
|
||||||
|
final Long templateId = this.fromApiJsonHelper.extractLongNamed(
|
||||||
|
HookApiConstants.templateIdParamName, element);
|
||||||
|
baseDataValidator.reset()
|
||||||
|
.parameter(HookApiConstants.templateIdParamName)
|
||||||
|
.value(templateId).notNull().integerGreaterThanZero();
|
||||||
|
}
|
||||||
|
|
||||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-15
@@ -5,20 +5,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.infrastructure.hooks.service;
|
package org.mifosplatform.infrastructure.hooks.service;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
|
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
|
||||||
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
|
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
|
||||||
import org.mifosplatform.infrastructure.hooks.data.Event;
|
import org.mifosplatform.infrastructure.hooks.data.*;
|
||||||
import org.mifosplatform.infrastructure.hooks.data.EventResultSetExtractor;
|
|
||||||
import org.mifosplatform.infrastructure.hooks.data.Field;
|
|
||||||
import org.mifosplatform.infrastructure.hooks.data.Grouping;
|
|
||||||
import org.mifosplatform.infrastructure.hooks.data.HookData;
|
|
||||||
import org.mifosplatform.infrastructure.hooks.data.HookTemplateData;
|
|
||||||
import org.mifosplatform.infrastructure.hooks.domain.Hook;
|
import org.mifosplatform.infrastructure.hooks.domain.Hook;
|
||||||
import org.mifosplatform.infrastructure.hooks.domain.HookRepository;
|
import org.mifosplatform.infrastructure.hooks.domain.HookRepository;
|
||||||
import org.mifosplatform.infrastructure.hooks.exception.HookNotFoundException;
|
import org.mifosplatform.infrastructure.hooks.exception.HookNotFoundException;
|
||||||
@@ -30,6 +20,11 @@ import org.springframework.jdbc.core.JdbcTemplate;
|
|||||||
import org.springframework.jdbc.core.RowMapper;
|
import org.springframework.jdbc.core.RowMapper;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class HookReadPlatformServiceImpl implements HookReadPlatformService {
|
public class HookReadPlatformServiceImpl implements HookReadPlatformService {
|
||||||
|
|
||||||
@@ -118,8 +113,10 @@ public class HookReadPlatformServiceImpl implements HookReadPlatformService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String schema() {
|
public String schema() {
|
||||||
return " h.id, s.name as name, h.name as display_name, h.is_active, h.created_date, h.lastmodified_date "
|
return " h.id, s.name as name, h.name as display_name, h.is_active, h.created_date,"
|
||||||
+ " from m_hook h inner join m_hook_templates s on h.template_id = s.id ";
|
+ " h.lastmodified_date, h.ugd_template_id, tp.name as ugd_template_name, "
|
||||||
|
+ "h.ugd_template_id from m_hook h inner join m_hook_templates s on h.template_id = s.id"
|
||||||
|
+ " inner join m_template tp on h.ugd_template_id = tp.id";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -135,11 +132,14 @@ public class HookReadPlatformServiceImpl implements HookReadPlatformService {
|
|||||||
"created_date");
|
"created_date");
|
||||||
final LocalDate updatedAt = JdbcSupport.getLocalDate(rs,
|
final LocalDate updatedAt = JdbcSupport.getLocalDate(rs,
|
||||||
"lastmodified_date");
|
"lastmodified_date");
|
||||||
|
final Long templateId = rs.getLong("ugd_template_id");
|
||||||
|
final String templateName = rs.getString("ugd_template_name");
|
||||||
final List<Event> registeredEvents = retrieveEvents(id);
|
final List<Event> registeredEvents = retrieveEvents(id);
|
||||||
final List<Field> config = retrieveConfig(id);
|
final List<Field> config = retrieveConfig(id);
|
||||||
|
|
||||||
return HookData.instance(id, name, displayname, isActive,
|
return HookData.instance(id, name, displayname, isActive,
|
||||||
createdAt, updatedAt, registeredEvents, config);
|
createdAt, updatedAt, templateId, registeredEvents, config,
|
||||||
|
templateName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Event> retrieveEvents(final Long hookId) {
|
private List<Event> retrieveEvents(final Long hookId) {
|
||||||
@@ -195,7 +195,8 @@ public class HookReadPlatformServiceImpl implements HookReadPlatformService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class TemplateMapper implements
|
private static final class TemplateMapper
|
||||||
|
implements
|
||||||
RowMapper<HookTemplateData> {
|
RowMapper<HookTemplateData> {
|
||||||
|
|
||||||
private final JdbcTemplate jdbcTemplate;
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
|||||||
+40
-29
@@ -5,22 +5,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.infrastructure.hooks.service;
|
package org.mifosplatform.infrastructure.hooks.service;
|
||||||
|
|
||||||
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.actionNameParamName;
|
import com.google.gson.JsonArray;
|
||||||
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.configParamName;
|
import com.google.gson.JsonObject;
|
||||||
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.contentTypeName;
|
|
||||||
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.entityNameParamName;
|
|
||||||
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.eventsParamName;
|
|
||||||
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.nameParamName;
|
|
||||||
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.payloadURLName;
|
|
||||||
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.webTemplateName;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
@@ -29,37 +15,37 @@ import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
|||||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||||
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
|
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||||
import org.mifosplatform.infrastructure.hooks.domain.Hook;
|
import org.mifosplatform.infrastructure.hooks.domain.*;
|
||||||
import org.mifosplatform.infrastructure.hooks.domain.HookConfiguration;
|
|
||||||
import org.mifosplatform.infrastructure.hooks.domain.HookRepository;
|
|
||||||
import org.mifosplatform.infrastructure.hooks.domain.HookResource;
|
|
||||||
import org.mifosplatform.infrastructure.hooks.domain.HookTemplate;
|
|
||||||
import org.mifosplatform.infrastructure.hooks.domain.HookTemplateRepository;
|
|
||||||
import org.mifosplatform.infrastructure.hooks.domain.Schema;
|
|
||||||
import org.mifosplatform.infrastructure.hooks.exception.HookNotFoundException;
|
import org.mifosplatform.infrastructure.hooks.exception.HookNotFoundException;
|
||||||
import org.mifosplatform.infrastructure.hooks.exception.HookTemplateNotFoundException;
|
import org.mifosplatform.infrastructure.hooks.exception.HookTemplateNotFoundException;
|
||||||
import org.mifosplatform.infrastructure.hooks.processor.ProcessorHelper;
|
import org.mifosplatform.infrastructure.hooks.processor.ProcessorHelper;
|
||||||
import org.mifosplatform.infrastructure.hooks.processor.WebHookService;
|
import org.mifosplatform.infrastructure.hooks.processor.WebHookService;
|
||||||
import org.mifosplatform.infrastructure.hooks.serialization.HookCommandFromApiJsonDeserializer;
|
import org.mifosplatform.infrastructure.hooks.serialization.HookCommandFromApiJsonDeserializer;
|
||||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.mifosplatform.template.domain.Template;
|
||||||
|
import org.mifosplatform.template.domain.TemplateRepository;
|
||||||
|
import org.mifosplatform.template.exception.TemplateNotFoundException;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.cache.annotation.CacheEvict;
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
import org.springframework.dao.DataIntegrityViolationException;
|
import org.springframework.dao.DataIntegrityViolationException;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import retrofit.RetrofitError;
|
import retrofit.RetrofitError;
|
||||||
|
|
||||||
import com.google.gson.JsonArray;
|
import java.util.*;
|
||||||
import com.google.gson.JsonObject;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.*;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class HookWritePlatformServiceJpaRepositoryImpl implements
|
public class HookWritePlatformServiceJpaRepositoryImpl
|
||||||
|
implements
|
||||||
HookWritePlatformService {
|
HookWritePlatformService {
|
||||||
|
|
||||||
private final PlatformSecurityContext context;
|
private final PlatformSecurityContext context;
|
||||||
private final HookRepository hookRepository;
|
private final HookRepository hookRepository;
|
||||||
private final HookTemplateRepository hookTemplateRepository;
|
private final HookTemplateRepository hookTemplateRepository;
|
||||||
|
private final TemplateRepository ugdTemplateRepository;
|
||||||
private final HookCommandFromApiJsonDeserializer fromApiJsonDeserializer;
|
private final HookCommandFromApiJsonDeserializer fromApiJsonDeserializer;
|
||||||
private final FromJsonHelper fromApiJsonHelper;
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
|
|
||||||
@@ -68,11 +54,13 @@ public class HookWritePlatformServiceJpaRepositoryImpl implements
|
|||||||
final PlatformSecurityContext context,
|
final PlatformSecurityContext context,
|
||||||
final HookRepository hookRepository,
|
final HookRepository hookRepository,
|
||||||
final HookTemplateRepository hookTemplateRepository,
|
final HookTemplateRepository hookTemplateRepository,
|
||||||
|
final TemplateRepository ugdTemplateRepository,
|
||||||
final HookCommandFromApiJsonDeserializer fromApiJsonDeserializer,
|
final HookCommandFromApiJsonDeserializer fromApiJsonDeserializer,
|
||||||
final FromJsonHelper fromApiJsonHelper) {
|
final FromJsonHelper fromApiJsonHelper) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.hookRepository = hookRepository;
|
this.hookRepository = hookRepository;
|
||||||
this.hookTemplateRepository = hookTemplateRepository;
|
this.hookTemplateRepository = hookTemplateRepository;
|
||||||
|
this.ugdTemplateRepository = ugdTemplateRepository;
|
||||||
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
|
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
|
||||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||||
}
|
}
|
||||||
@@ -95,8 +83,17 @@ public class HookWritePlatformServiceJpaRepositoryImpl implements
|
|||||||
final JsonArray events = command
|
final JsonArray events = command
|
||||||
.arrayOfParameterNamed(eventsParamName);
|
.arrayOfParameterNamed(eventsParamName);
|
||||||
final Set<HookResource> allEvents = assembleSetOfEvents(events);
|
final Set<HookResource> allEvents = assembleSetOfEvents(events);
|
||||||
|
Template ugdTemplate = null;
|
||||||
|
if (command.hasParameter(templateIdParamName)) {
|
||||||
|
final Long ugdTemplateId = command
|
||||||
|
.longValueOfParameterNamed(templateIdParamName);
|
||||||
|
ugdTemplate = this.ugdTemplateRepository.findOne(ugdTemplateId);
|
||||||
|
if (ugdTemplate == null) {
|
||||||
|
throw new TemplateNotFoundException(ugdTemplateId);
|
||||||
|
}
|
||||||
|
}
|
||||||
final Hook hook = Hook.fromJson(command, template, config,
|
final Hook hook = Hook.fromJson(command, template, config,
|
||||||
allEvents);
|
allEvents, ugdTemplate);
|
||||||
|
|
||||||
validateHookRules(template, config, allEvents);
|
validateHookRules(template, config, allEvents);
|
||||||
|
|
||||||
@@ -128,6 +125,18 @@ public class HookWritePlatformServiceJpaRepositoryImpl implements
|
|||||||
|
|
||||||
if (!changes.isEmpty()) {
|
if (!changes.isEmpty()) {
|
||||||
|
|
||||||
|
if (changes.containsKey(templateIdParamName)) {
|
||||||
|
final Long ugdTemplateId = command
|
||||||
|
.longValueOfParameterNamed(templateIdParamName);
|
||||||
|
final Template ugdTemplate = this.ugdTemplateRepository
|
||||||
|
.findOne(ugdTemplateId);
|
||||||
|
if (ugdTemplate == null) {
|
||||||
|
changes.remove(templateIdParamName);
|
||||||
|
throw new TemplateNotFoundException(ugdTemplateId);
|
||||||
|
}
|
||||||
|
hook.updateUgdTemplate(ugdTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
if (changes.containsKey(eventsParamName)) {
|
if (changes.containsKey(eventsParamName)) {
|
||||||
final Set<HookResource> events = assembleSetOfEvents(command
|
final Set<HookResource> events = assembleSetOfEvents(command
|
||||||
.arrayOfParameterNamed(eventsParamName));
|
.arrayOfParameterNamed(eventsParamName));
|
||||||
@@ -311,7 +320,9 @@ public class HookWritePlatformServiceJpaRepositoryImpl implements
|
|||||||
if (!found) {
|
if (!found) {
|
||||||
final String errorMessage = "required.config.field."
|
final String errorMessage = "required.config.field."
|
||||||
+ "not.provided";
|
+ "not.provided";
|
||||||
baseDataValidator.reset().value(field.getFieldName())
|
baseDataValidator
|
||||||
|
.reset()
|
||||||
|
.value(field.getFieldName())
|
||||||
.failWithCodeNoParameterAddedToErrorCode(
|
.failWithCodeNoParameterAddedToErrorCode(
|
||||||
errorMessage);
|
errorMessage);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ public class Template extends AbstractPersistable<Long> {
|
|||||||
@OneToMany(targetEntity = TemplateMapper.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
@OneToMany(targetEntity = TemplateMapper.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||||
private List<TemplateMapper> mappers;
|
private List<TemplateMapper> mappers;
|
||||||
|
|
||||||
public Template(final String name, final String text, final TemplateEntity entity, final TemplateType type,
|
public Template(final String name, final String text,
|
||||||
|
final TemplateEntity entity, final TemplateType type,
|
||||||
final List<TemplateMapper> mappers) {
|
final List<TemplateMapper> mappers) {
|
||||||
this.name = StringUtils.defaultIfEmpty(name, null);
|
this.name = StringUtils.defaultIfEmpty(name, null);
|
||||||
this.entity = entity;
|
this.entity = entity;
|
||||||
@@ -65,16 +66,29 @@ public class Template extends AbstractPersistable<Long> {
|
|||||||
public static Template fromJson(final JsonCommand command) {
|
public static Template fromJson(final JsonCommand command) {
|
||||||
final String name = command.stringValueOfParameterNamed("name");
|
final String name = command.stringValueOfParameterNamed("name");
|
||||||
final String text = command.stringValueOfParameterNamed("text");
|
final String text = command.stringValueOfParameterNamed("text");
|
||||||
final TemplateEntity entity = TemplateEntity.values()[command.integerValueSansLocaleOfParameterNamed("entity")];
|
final TemplateEntity entity = TemplateEntity.values()[command
|
||||||
final TemplateType type = TemplateType.values()[command.integerValueSansLocaleOfParameterNamed("type")];
|
.integerValueSansLocaleOfParameterNamed("entity")];
|
||||||
|
final int templateTypeId = command
|
||||||
|
.integerValueSansLocaleOfParameterNamed("type");
|
||||||
|
TemplateType type = null;
|
||||||
|
switch (templateTypeId) {
|
||||||
|
case 0 :
|
||||||
|
type = TemplateType.DOCUMENT;
|
||||||
|
break;
|
||||||
|
case 2 :
|
||||||
|
type = TemplateType.SMS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
final JsonArray array = command.arrayOfParameterNamed("mappers");
|
final JsonArray array = command.arrayOfParameterNamed("mappers");
|
||||||
|
|
||||||
final List<TemplateMapper> mappersList = new ArrayList<>();
|
final List<TemplateMapper> mappersList = new ArrayList<>();
|
||||||
|
|
||||||
for (final JsonElement element : array) {
|
for (final JsonElement element : array) {
|
||||||
mappersList.add(new TemplateMapper(element.getAsJsonObject().get("mappersorder").getAsInt(), element.getAsJsonObject()
|
mappersList.add(new TemplateMapper(element.getAsJsonObject()
|
||||||
.get("mapperskey").getAsString(), element.getAsJsonObject().get("mappersvalue").getAsString()));
|
.get("mappersorder").getAsInt(), element.getAsJsonObject()
|
||||||
|
.get("mapperskey").getAsString(), element.getAsJsonObject()
|
||||||
|
.get("mappersvalue").getAsString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Template(name, text, entity, type, mappersList);
|
return new Template(name, text, entity, type, mappersList);
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ import com.google.gson.annotations.SerializedName;
|
|||||||
public enum TemplateType {
|
public enum TemplateType {
|
||||||
|
|
||||||
@SerializedName("Document")
|
@SerializedName("Document")
|
||||||
DOCUMENT(0, "Document");
|
DOCUMENT(0, "Document"), @SerializedName("SMS")
|
||||||
|
SMS(2, "SMS");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @SerializedName("E-Mail") EMAIL(1, "E-Mail"),
|
* @SerializedName("E-Mail") EMAIL(1, "E-Mail")
|
||||||
* @SerializedName("SMS") SMS(2, "SMS");
|
|
||||||
*/
|
*/
|
||||||
private int id;
|
private int id;
|
||||||
private String name;
|
private String name;
|
||||||
|
|||||||
+32
-10
@@ -44,7 +44,9 @@ public class JpaTemplateDomainService implements TemplateDomainService {
|
|||||||
@Override
|
@Override
|
||||||
public Template findOneById(final Long id) {
|
public Template findOneById(final Long id) {
|
||||||
final Template template = this.templateRepository.findOne(id);
|
final Template template = this.templateRepository.findOne(id);
|
||||||
if (template == null) { throw new TemplateNotFoundException(id); }
|
if (template == null) {
|
||||||
|
throw new TemplateNotFoundException(id);
|
||||||
|
}
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,12 +60,14 @@ public class JpaTemplateDomainService implements TemplateDomainService {
|
|||||||
final Template template = Template.fromJson(command);
|
final Template template = Template.fromJson(command);
|
||||||
|
|
||||||
this.templateRepository.saveAndFlush(template);
|
this.templateRepository.saveAndFlush(template);
|
||||||
return new CommandProcessingResultBuilder().withEntityId(template.getId()).build();
|
return new CommandProcessingResultBuilder().withEntityId(
|
||||||
|
template.getId()).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult updateTemplate(final Long templateId, final JsonCommand command) {
|
public CommandProcessingResult updateTemplate(final Long templateId,
|
||||||
|
final JsonCommand command) {
|
||||||
// FIXME - no validation here of the data in the command object, is
|
// FIXME - no validation here of the data in the command object, is
|
||||||
// name, text populated etc
|
// name, text populated etc
|
||||||
// FIXME - handle cases where data integrity constraints are fired from
|
// FIXME - handle cases where data integrity constraints are fired from
|
||||||
@@ -72,20 +76,36 @@ public class JpaTemplateDomainService implements TemplateDomainService {
|
|||||||
final Template template = findOneById(templateId);
|
final Template template = findOneById(templateId);
|
||||||
template.setName(command.stringValueOfParameterNamed(PROPERTY_NAME));
|
template.setName(command.stringValueOfParameterNamed(PROPERTY_NAME));
|
||||||
template.setText(command.stringValueOfParameterNamed(PROPERTY_TEXT));
|
template.setText(command.stringValueOfParameterNamed(PROPERTY_TEXT));
|
||||||
template.setEntity(TemplateEntity.values()[command.integerValueSansLocaleOfParameterNamed(PROPERTY_ENTITY)]);
|
template.setEntity(TemplateEntity.values()[command
|
||||||
template.setType(TemplateType.values()[command.integerValueSansLocaleOfParameterNamed(PROPERTY_TYPE)]);
|
.integerValueSansLocaleOfParameterNamed(PROPERTY_ENTITY)]);
|
||||||
|
final int templateTypeId = command
|
||||||
|
.integerValueSansLocaleOfParameterNamed(PROPERTY_TYPE);
|
||||||
|
TemplateType type = null;
|
||||||
|
switch (templateTypeId) {
|
||||||
|
case 0 :
|
||||||
|
type = TemplateType.DOCUMENT;
|
||||||
|
break;
|
||||||
|
case 2 :
|
||||||
|
type = TemplateType.SMS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
template.setType(type);
|
||||||
|
|
||||||
final JsonArray array = command.arrayOfParameterNamed("mappers");
|
final JsonArray array = command.arrayOfParameterNamed("mappers");
|
||||||
final List<TemplateMapper> mappersList = new ArrayList<>();
|
final List<TemplateMapper> mappersList = new ArrayList<>();
|
||||||
for (final JsonElement element : array) {
|
for (final JsonElement element : array) {
|
||||||
mappersList.add(new TemplateMapper(element.getAsJsonObject().get("mappersorder").getAsInt(), element.getAsJsonObject()
|
mappersList.add(new TemplateMapper(element.getAsJsonObject()
|
||||||
.get("mapperskey").getAsString(), element.getAsJsonObject().get("mappersvalue").getAsString()));
|
.get("mappersorder").getAsInt(), element.getAsJsonObject()
|
||||||
|
.get("mapperskey").getAsString(), element.getAsJsonObject()
|
||||||
|
.get("mappersvalue").getAsString()));
|
||||||
}
|
}
|
||||||
template.setMappers(mappersList);
|
template.setMappers(mappersList);
|
||||||
|
|
||||||
this.templateRepository.saveAndFlush(template);
|
this.templateRepository.saveAndFlush(template);
|
||||||
|
|
||||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(template.getId()).build();
|
return new CommandProcessingResultBuilder()
|
||||||
|
.withCommandId(command.commandId())
|
||||||
|
.withEntityId(template.getId()).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@@ -95,7 +115,8 @@ public class JpaTemplateDomainService implements TemplateDomainService {
|
|||||||
|
|
||||||
this.templateRepository.delete(template);
|
this.templateRepository.delete(template);
|
||||||
|
|
||||||
return new CommandProcessingResultBuilder().withEntityId(templateId).build();
|
return new CommandProcessingResultBuilder().withEntityId(templateId)
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@@ -105,7 +126,8 @@ public class JpaTemplateDomainService implements TemplateDomainService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Template> getAllByEntityAndType(final TemplateEntity entity, final TemplateType type) {
|
public List<Template> getAllByEntityAndType(final TemplateEntity entity,
|
||||||
|
final TemplateType type) {
|
||||||
|
|
||||||
return this.templateRepository.findByEntityAndType(entity, type);
|
return this.templateRepository.findByEntityAndType(entity, type);
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-10
@@ -23,6 +23,7 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.codehaus.jackson.map.ObjectMapper;
|
import org.codehaus.jackson.map.ObjectMapper;
|
||||||
|
import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil;
|
||||||
import org.mifosplatform.template.domain.Template;
|
import org.mifosplatform.template.domain.Template;
|
||||||
import org.mifosplatform.template.domain.TemplateFunctions;
|
import org.mifosplatform.template.domain.TemplateFunctions;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
@@ -37,21 +38,30 @@ public class TemplateMergeService {
|
|||||||
|
|
||||||
// private final FromJsonHelper fromApiJsonHelper;
|
// private final FromJsonHelper fromApiJsonHelper;
|
||||||
private Map<String, Object> scopes;
|
private Map<String, Object> scopes;
|
||||||
|
private String authToken;
|
||||||
|
|
||||||
// @Autowired
|
// @Autowired
|
||||||
// public TemplateMergeService(final FromJsonHelper fromApiJsonHelper) {
|
// public TemplateMergeService(final FromJsonHelper fromApiJsonHelper) {
|
||||||
// this.fromApiJsonHelper = fromApiJsonHelper;
|
// this.fromApiJsonHelper = fromApiJsonHelper;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
public String compile(final Template template, final Map<String, Object> scopes) throws MalformedURLException, IOException {
|
public void setAuthToken(final String authToken) {
|
||||||
|
this.authToken = authToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String compile(final Template template,
|
||||||
|
final Map<String, Object> scopes) throws MalformedURLException,
|
||||||
|
IOException {
|
||||||
|
|
||||||
this.scopes = scopes;
|
this.scopes = scopes;
|
||||||
this.scopes.put("static", new TemplateFunctions());
|
this.scopes.put("static", new TemplateFunctions());
|
||||||
|
|
||||||
final MustacheFactory mf = new DefaultMustacheFactory();
|
final MustacheFactory mf = new DefaultMustacheFactory();
|
||||||
final Mustache mustache = mf.compile(new StringReader(template.getText()), template.getName());
|
final Mustache mustache = mf.compile(
|
||||||
|
new StringReader(template.getText()), template.getName());
|
||||||
|
|
||||||
final Map<String, Object> mappers = getCompiledMapFromMappers(template.getMappersAsMap());
|
final Map<String, Object> mappers = getCompiledMapFromMappers(template
|
||||||
|
.getMappersAsMap());
|
||||||
this.scopes.putAll(mappers);
|
this.scopes.putAll(mappers);
|
||||||
|
|
||||||
final StringWriter stringWriter = new StringWriter();
|
final StringWriter stringWriter = new StringWriter();
|
||||||
@@ -60,14 +70,16 @@ public class TemplateMergeService {
|
|||||||
return stringWriter.toString();
|
return stringWriter.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> getCompiledMapFromMappers(final Map<String, String> data) {
|
private Map<String, Object> getCompiledMapFromMappers(
|
||||||
|
final Map<String, String> data) {
|
||||||
|
|
||||||
final MustacheFactory mf = new DefaultMustacheFactory();
|
final MustacheFactory mf = new DefaultMustacheFactory();
|
||||||
|
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
for (final Map.Entry<String, String> entry : data.entrySet()) {
|
for (final Map.Entry<String, String> entry : data.entrySet()) {
|
||||||
|
|
||||||
final Mustache mappersMustache = mf.compile(new StringReader(entry.getValue()), "");
|
final Mustache mappersMustache = mf.compile(new StringReader(
|
||||||
|
entry.getValue()), "");
|
||||||
final StringWriter stringWriter = new StringWriter();
|
final StringWriter stringWriter = new StringWriter();
|
||||||
|
|
||||||
mappersMustache.execute(stringWriter, this.scopes);
|
mappersMustache.execute(stringWriter, this.scopes);
|
||||||
@@ -88,11 +100,13 @@ public class TemplateMergeService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Map<String, Object> getMapFromUrl(final String url) throws MalformedURLException, IOException {
|
private Map<String, Object> getMapFromUrl(final String url)
|
||||||
|
throws MalformedURLException, IOException {
|
||||||
|
|
||||||
final HttpURLConnection connection = getConnection(url);
|
final HttpURLConnection connection = getConnection(url);
|
||||||
|
|
||||||
final String response = getStringFromInputStream(connection.getInputStream());
|
final String response = getStringFromInputStream(connection
|
||||||
|
.getInputStream());
|
||||||
HashMap<String, Object> result = new HashMap<>();
|
HashMap<String, Object> result = new HashMap<>();
|
||||||
if (connection.getContentType().equals("text/plain")) {
|
if (connection.getContentType().equals("text/plain")) {
|
||||||
result.put("src", response);
|
result.put("src", response);
|
||||||
@@ -104,20 +118,29 @@ public class TemplateMergeService {
|
|||||||
|
|
||||||
private HttpURLConnection getConnection(final String url) {
|
private HttpURLConnection getConnection(final String url) {
|
||||||
|
|
||||||
final String name = SecurityContextHolder.getContext().getAuthentication().getName();
|
if (this.authToken == null) {
|
||||||
final String password = SecurityContextHolder.getContext().getAuthentication().getCredentials().toString();
|
final String name = SecurityContextHolder.getContext()
|
||||||
|
.getAuthentication().getName();
|
||||||
|
final String password = SecurityContextHolder.getContext()
|
||||||
|
.getAuthentication().getCredentials().toString();
|
||||||
|
|
||||||
Authenticator.setDefault(new Authenticator() {
|
Authenticator.setDefault(new Authenticator() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PasswordAuthentication getPasswordAuthentication() {
|
protected PasswordAuthentication getPasswordAuthentication() {
|
||||||
return new PasswordAuthentication(name, password.toCharArray());
|
return new PasswordAuthentication(name, password
|
||||||
|
.toCharArray());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
HttpURLConnection connection = null;
|
HttpURLConnection connection = null;
|
||||||
try {
|
try {
|
||||||
connection = (HttpURLConnection) new URL(url).openConnection();
|
connection = (HttpURLConnection) new URL(url).openConnection();
|
||||||
|
if (this.authToken != null) {
|
||||||
|
connection.setRequestProperty("Authorization", "Basic "
|
||||||
|
+ this.authToken);
|
||||||
|
}
|
||||||
TrustModifier.relaxHostChecking(connection);
|
TrustModifier.relaxHostChecking(connection);
|
||||||
|
|
||||||
connection.setDoInput(true);
|
connection.setDoInput(true);
|
||||||
|
|||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE `m_hook` ADD COLUMN `ugd_template_id` bigint(20) NOT NULL;
|
||||||
|
ALTER TABLE `m_hook` ADD CONSTRAINT `fk_ugd_template_id` FOREIGN KEY (`ugd_template_id`) REFERENCES `m_template` (`id`);
|
||||||
Reference in New Issue
Block a user