@@ -1991,6 +1991,22 @@
|
|||||||
Global Configuration</a></td>
|
Global Configuration</a></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><a href="#configs_hooks">Hooks</a></td>
|
||||||
|
<td>hooks</td>
|
||||||
|
<td><a href="#configs_hooks_create">Create a Hook</a></td>
|
||||||
|
<td><a href="#configs_hooks_retrieve">List Hooks</a></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>hooks/{hookId}</td>
|
||||||
|
<td></td>
|
||||||
|
<td><a href="#configs_hook_retrieve">Retrieve a Hook</a></td>
|
||||||
|
<td><a href="#configs_hook_update">Update a Hook</a></td>
|
||||||
|
<td><a href="#configs_hook_delete">Delete a Hook</a></td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="#configs_codes">Codes</a></td>
|
<td><a href="#configs_codes">Codes</a></td>
|
||||||
<td>codes</td>
|
<td>codes</td>
|
||||||
@@ -2446,6 +2462,10 @@
|
|||||||
<td>users/template</td>
|
<td>users/template</td>
|
||||||
<td><a href="#users_template">Retrieve User Template</a></td>
|
<td><a href="#users_template">Retrieve User Template</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>hooks/template</td>
|
||||||
|
<td><a href="#hooks_template">Retrieve Hooks Template</a></td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>audits/searchtemplate</td>
|
<td>audits/searchtemplate</td>
|
||||||
<td><a href="#audits_searchtemplate">Retrieve Audit Search Template</a></td>
|
<td><a href="#audits_searchtemplate">Retrieve Audit Search Template</a></td>
|
||||||
@@ -13335,6 +13355,300 @@ Request Body:
|
|||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<a id="configs_hooks" name="configs_hooks" class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h3>Hooks</h3>
|
||||||
|
<p>Hooks are a mechanism to trigger custom code on the occurence of events.</p>
|
||||||
|
<p>Each template during hook creation represents custom behaviour on what actions should be taken on the triggering of a registered event for a hook. The action taken might be firing a HTTP request to another server or execute internal code.
|
||||||
|
<p />
|
||||||
|
<p>In order for MifosX to send webhook payloads, your server needs to be accessible from the Internet. MifosX will send along a few HTTP headers to differentiate between event types.</p>
|
||||||
|
<p>X-Mifos-Entity - The entity type that was triggered. <br/>
|
||||||
|
X-Mifos-Action - The action triggered on the entity type. <br/>
|
||||||
|
X-Mifos-Platform-TenantId - The tenant which experienced the event.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a id="configs_hooks_create" name="configs_hooks_create"
|
||||||
|
class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h4>Create a Hook</h4>
|
||||||
|
<p>The following parameters can be passed for the creation of a hook :-
|
||||||
|
<ul>
|
||||||
|
<li> name - string - Required. The name of the template that is being called. (See /hooks/template for the list of valid hook names.)</li>
|
||||||
|
<li> isActive - boolean - Determines whether the hook is actually triggered.</li>
|
||||||
|
<li> events - array - Determines what events the hook is triggered for.
|
||||||
|
</li>
|
||||||
|
<li> config - hash - Required. Key/value pairs to provide settings for this hook. These settings vary between the templates.</li>
|
||||||
|
<ul></p>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
POST https://DomainName/api/v1/hooks
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
POST hooks
|
||||||
|
Content-Type: application/json
|
||||||
|
Request Body:
|
||||||
|
{
|
||||||
|
"name": "Web",
|
||||||
|
"isActive": true,
|
||||||
|
"displayName": "Kremlin",
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"actionName": "DISBURSE",
|
||||||
|
"entityName": "LOAN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"actionName": "REPAYMENT",
|
||||||
|
"entityName": "LOAN"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"config": {
|
||||||
|
"Payload URL": "http://example.com/webhook",
|
||||||
|
"Content Type": "json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"resourceId": 4
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a id="configs_hooks_retrieve" name="configs_hooks_retrieve"
|
||||||
|
class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h4>Retrieve Hooks</h4>
|
||||||
|
<p>Returns the list of hooks.</p>
|
||||||
|
<p>Example Requests:</p>
|
||||||
|
<div class=apiClick>hooks</div>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
GET https://DomainName/api/v1/hooks
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Web",
|
||||||
|
"displayName": "Kremlin",
|
||||||
|
"isActive": true,
|
||||||
|
"createdAt": [2014, 9, 16],
|
||||||
|
"updatedAt": [2014, 9, 16],
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"actionName": "DISBURSE",
|
||||||
|
"entityName": "LOAN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"actionName": "REPAYMENT",
|
||||||
|
"entityName": "LOAN"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"config": [
|
||||||
|
{
|
||||||
|
"fieldName": "Content Type",
|
||||||
|
"fieldValue": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldName": "Payload URL",
|
||||||
|
"fieldValue": "https://abc.com/api"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a id="configs_hook_retrieve" name="configs_hook_retrieve"
|
||||||
|
class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h4>Retrieve a Hook</h4>
|
||||||
|
<p>Returns the details of a Hook.</p>
|
||||||
|
<p>Example Requests:</p>
|
||||||
|
<div class=apiClick>hooks/1</div>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
GET https://DomainName/api/v1/hooks/{hookId}
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Web",
|
||||||
|
"displayName": "Kremlin",
|
||||||
|
"isActive": true,
|
||||||
|
"createdAt": [2014, 9, 16],
|
||||||
|
"updatedAt": [2014, 9, 16],
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"actionName": "DISBURSE",
|
||||||
|
"entityName": "LOAN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"actionName": "REPAYMENT",
|
||||||
|
"entityName": "LOAN"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"config": [
|
||||||
|
{
|
||||||
|
"fieldName": "Content Type",
|
||||||
|
"fieldValue": "json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldName": "Payload URL",
|
||||||
|
"fieldValue": "https://abc.com/api"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a id="configs_hook_update" name="configs_hook_update"
|
||||||
|
class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h4>Update a Hook</h4>
|
||||||
|
<p>Updates the details of a hook.</p>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
PUT https://DomainName/api/v1/hooks/{hookId}
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
PUT hooks/4
|
||||||
|
Content-Type: application/json
|
||||||
|
Request Body:
|
||||||
|
{
|
||||||
|
"name": "Web",
|
||||||
|
"isActive": true,
|
||||||
|
"displayName": "Local SMS Provider",
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"actionName": "DISBURSE",
|
||||||
|
"entityName": "LOAN"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"config": {
|
||||||
|
"Payload URL": "http://changed.com/sms",
|
||||||
|
"Content Type": "json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"resourceId": 4,
|
||||||
|
"changes": {
|
||||||
|
"displayName": "Local SMS Provider",
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"actionName": "DISBURSE",
|
||||||
|
"entityName": "LOAN"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"config": {
|
||||||
|
"Payload URL": "http://changed.com/sms",
|
||||||
|
"Content Type": "json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a id="configs_hook_delete" name="configs_hook_delete"
|
||||||
|
class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h4>Delete a Hook</h4>
|
||||||
|
<p>Deletes a hook.</p>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
DELETE https://DomainName/api/v1/hooks/{hookId}
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
DELETE hooks/4
|
||||||
|
Content-Type: application/json
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"resourceId": 4
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a id="hooks_template" name="hooks_template" class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h4>Retrieve Hooks Template</h4>
|
||||||
|
<p>This is a convenience resource. It can be useful when
|
||||||
|
building maintenance user interface screens for client
|
||||||
|
applications. The template data returned consists of any or all
|
||||||
|
of:
|
||||||
|
<ul>
|
||||||
|
<li class=normalli>Field Defaults</li>
|
||||||
|
<li class=normalli>Allowed Value Lists</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
<p>Example Request:</p>
|
||||||
|
<div class=apiClick>hooks/template</div>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
GET https://DomainName/api/v1/hooks/template
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"templates": [
|
||||||
|
{
|
||||||
|
"name": "Web",
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"fieldName": "Content Type",
|
||||||
|
"fieldType": "string",
|
||||||
|
"optional": true,
|
||||||
|
"placeholder": "json / form"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldName": "Payload URL",
|
||||||
|
"fieldType": "string",
|
||||||
|
"optional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
},...
|
||||||
|
],
|
||||||
|
"groupings": [
|
||||||
|
{
|
||||||
|
"name": "jobs",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"name": "SCHEDULER",
|
||||||
|
"actions": ["EXECUTEJOB", "UPDATE"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
},...
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
},...
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a id="configs_codes" name="configs_codes" class="old-syle-anchor"> </a>
|
<a id="configs_codes" name="configs_codes" class="old-syle-anchor"> </a>
|
||||||
<div class="method-section">
|
<div class="method-section">
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
-- -----------------------------------------------------
|
||||||
|
-- Table `m_hook_templates`
|
||||||
|
-- -----------------------------------------------------
|
||||||
|
CREATE TABLE IF NOT EXISTS `m_hook_templates` (
|
||||||
|
`id` SMALLINT(6) NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` VARCHAR(45) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`))
|
||||||
|
ENGINE = InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
|
||||||
|
-- -----------------------------------------------------
|
||||||
|
-- Table `m_hook`
|
||||||
|
-- -----------------------------------------------------
|
||||||
|
CREATE TABLE IF NOT EXISTS `m_hook` (
|
||||||
|
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||||
|
`template_id` SMALLINT(6) NOT NULL,
|
||||||
|
`is_active` SMALLINT(3) NOT NULL DEFAULT 1,
|
||||||
|
`name` VARCHAR(45) NOT NULL,
|
||||||
|
`createdby_id` bigint(20) DEFAULT NULL,
|
||||||
|
`created_date` datetime DEFAULT NULL,
|
||||||
|
`lastmodifiedby_id` bigint(20) DEFAULT NULL,
|
||||||
|
`lastmodified_date` datetime DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `fk_template_id_idx` (`template_id` ASC),
|
||||||
|
CONSTRAINT `fk_template_id`
|
||||||
|
FOREIGN KEY (`template_id`)
|
||||||
|
REFERENCES `m_hook_templates` (`id`))
|
||||||
|
ENGINE = InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
|
||||||
|
-- -----------------------------------------------------
|
||||||
|
-- Table `m_hook_schema`
|
||||||
|
-- -----------------------------------------------------
|
||||||
|
CREATE TABLE IF NOT EXISTS `m_hook_schema` (
|
||||||
|
`id` SMALLINT(6) NOT NULL AUTO_INCREMENT,
|
||||||
|
`hook_template_id` SMALLINT(6) NOT NULL,
|
||||||
|
`field_type` VARCHAR(45) NOT NULL,
|
||||||
|
`field_name` VARCHAR(100) NOT NULL,
|
||||||
|
`placeholder` VARCHAR(100) DEFAULT NULL,
|
||||||
|
`optional` TINYINT(3) NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `fk_hook_template_id_idx` (`hook_template_id` ASC),
|
||||||
|
CONSTRAINT `fk_hook_template_id`
|
||||||
|
FOREIGN KEY (`hook_template_id`)
|
||||||
|
REFERENCES `m_hook_templates` (`id`))
|
||||||
|
ENGINE = InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
|
||||||
|
-- -----------------------------------------------------
|
||||||
|
-- Table `m_hook_registered_events`
|
||||||
|
-- -----------------------------------------------------
|
||||||
|
CREATE TABLE IF NOT EXISTS `m_hook_registered_events` (
|
||||||
|
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||||
|
`hook_id` BIGINT(20) NOT NULL,
|
||||||
|
`entity_name` VARCHAR(45) NOT NULL,
|
||||||
|
`action_name` VARCHAR(45) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `fk_hook_id_idx` (`hook_id` ASC),
|
||||||
|
CONSTRAINT `fk_hook_idc`
|
||||||
|
FOREIGN KEY (`hook_id`)
|
||||||
|
REFERENCES `m_hook` (`id`))
|
||||||
|
ENGINE = InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
|
||||||
|
-- -----------------------------------------------------
|
||||||
|
-- Table `m_hook_configuration`
|
||||||
|
-- -----------------------------------------------------
|
||||||
|
CREATE TABLE IF NOT EXISTS `m_hook_configuration` (
|
||||||
|
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||||
|
`hook_id` BIGINT(20) NULL,
|
||||||
|
`field_type` VARCHAR(45) NOT NULL,
|
||||||
|
`field_name` VARCHAR(100) NOT NULL,
|
||||||
|
`field_value` VARCHAR(100) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `fk_hook_id_idx` (`hook_id` ASC),
|
||||||
|
CONSTRAINT `fk_hook_id_cfg`
|
||||||
|
FOREIGN KEY (`hook_id`)
|
||||||
|
REFERENCES `m_hook` (`id`))
|
||||||
|
ENGINE = InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
insert into `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
|
||||||
|
VALUES ('configuration', 'CREATE_HOOK', 'HOOK', 'CREATE', 0);
|
||||||
|
insert into `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
|
||||||
|
VALUES ('configuration', 'READ_HOOK', 'HOOK', 'READ', 0);
|
||||||
|
insert into `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
|
||||||
|
VALUES ('configuration', 'UPDATE_HOOK', 'HOOK', 'UPDATE', 0);
|
||||||
|
insert into `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
|
||||||
|
VALUES ('configuration', 'DELETE_HOOK', 'HOOK', 'DELETE', 0);
|
||||||
|
|
||||||
|
insert into m_hook_templates values(1, "Web");
|
||||||
|
insert into m_hook_templates values(2, "SMS Bridge");
|
||||||
|
|
||||||
|
INSERT INTO `m_hook_schema` (`hook_template_id`, `field_type`, `field_name`, `optional`)
|
||||||
|
VALUES (1, 'string', 'Payload URL', 0);
|
||||||
|
INSERT INTO `m_hook_schema` (`hook_template_id`, `field_type`, `field_name`, `placeholder`, `optional`)
|
||||||
|
VALUES (1, 'string', 'Content Type', 'json / form', 0);
|
||||||
|
INSERT INTO `m_hook_schema` (`hook_template_id`, `field_type`, `field_name`, `optional`)
|
||||||
|
VALUES (2, 'string', 'Payload URL', 0);
|
||||||
|
INSERT INTO `m_hook_schema` (`hook_template_id`, `field_type`, `field_name`, `optional`)
|
||||||
|
VALUES (2, 'string', 'SMS Provider', 0);
|
||||||
|
INSERT INTO `m_hook_schema` (`hook_template_id`, `field_type`, `field_name`, `optional`)
|
||||||
|
VALUES (2, 'string', 'Phone Number', 0);
|
||||||
|
INSERT INTO `m_hook_schema` (`hook_template_id`, `field_type`, `field_name`, `optional`)
|
||||||
|
VALUES (2, 'string', 'SMS Provider Token', 0);
|
||||||
|
INSERT INTO `m_hook_schema` (`hook_template_id`, `field_type`, `field_name`, `optional`)
|
||||||
|
VALUES (2, 'string', 'SMS Provider Account Id', 0);
|
||||||
|
|
||||||
|
|
||||||
@@ -90,6 +90,11 @@ dependencies {
|
|||||||
[group: 'com.sun.jersey.contribs', name: 'jersey-spring', version: jerseyVersion],
|
[group: 'com.sun.jersey.contribs', name: 'jersey-spring', version: jerseyVersion],
|
||||||
[group: 'com.sun.jersey.contribs', name: 'jersey-multipart', version: jerseyVersion],
|
[group: 'com.sun.jersey.contribs', name: 'jersey-multipart', version: jerseyVersion],
|
||||||
|
|
||||||
|
[group: 'com.squareup.retrofit', name: 'retrofit', version: '1.6.1'],
|
||||||
|
[group: 'com.squareup.okhttp', name: 'okhttp', version: '2.0.0'],
|
||||||
|
[group: 'com.squareup.okhttp', name: 'okhttp-urlconnection', version: '2.0.0'],
|
||||||
|
|
||||||
|
|
||||||
[group: 'com.google.code.gson', name: 'gson', version: '2.2.4'],
|
[group: 'com.google.code.gson', name: 'gson', version: '2.2.4'],
|
||||||
[group: 'com.google.guava', name: 'guava', version: '15.0'],
|
[group: 'com.google.guava', name: 'guava', version: '15.0'],
|
||||||
|
|
||||||
|
|||||||
@@ -233,6 +233,10 @@ public class CommandWrapper {
|
|||||||
return this.entityName.equalsIgnoreCase("USER");
|
return this.entityName.equalsIgnoreCase("USER");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isHookResource() {
|
||||||
|
return this.entityName.equalsIgnoreCase("HOOK");
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isCurrencyResource() {
|
public boolean isCurrencyResource() {
|
||||||
return this.entityName.equalsIgnoreCase("CURRENCY");
|
return this.entityName.equalsIgnoreCase("CURRENCY");
|
||||||
}
|
}
|
||||||
|
|||||||
+24
@@ -302,6 +302,30 @@ public class CommandWrapperBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder createHook() {
|
||||||
|
this.actionName = "CREATE";
|
||||||
|
this.entityName = "HOOK";
|
||||||
|
this.entityId = null;
|
||||||
|
this.href = "/hooks/template";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder updateHook(final Long hookId) {
|
||||||
|
this.actionName = "UPDATE";
|
||||||
|
this.entityName = "HOOK";
|
||||||
|
this.entityId = hookId;
|
||||||
|
this.href = "/hooks/" + hookId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder deleteHook(final Long hookId) {
|
||||||
|
this.actionName = "DELETE";
|
||||||
|
this.entityName = "HOOK";
|
||||||
|
this.entityId = hookId;
|
||||||
|
this.href = "/hooks/" + hookId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public CommandWrapperBuilder createCharge() {
|
public CommandWrapperBuilder createCharge() {
|
||||||
this.actionName = "CREATE";
|
this.actionName = "CREATE";
|
||||||
this.entityName = "CHARGE";
|
this.entityName = "CHARGE";
|
||||||
|
|||||||
+38
-2
@@ -19,6 +19,9 @@ import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
|||||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||||
import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer;
|
import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer;
|
||||||
|
import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil;
|
||||||
|
import org.mifosplatform.infrastructure.hooks.event.HookEvent;
|
||||||
|
import org.mifosplatform.infrastructure.hooks.event.HookEventSource;
|
||||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||||
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;
|
||||||
@@ -32,17 +35,21 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
|||||||
private PlatformSecurityContext context;
|
private PlatformSecurityContext context;
|
||||||
private final ApplicationContext applicationContext;
|
private final ApplicationContext applicationContext;
|
||||||
private final ToApiJsonSerializer<Map<String, Object>> toApiJsonSerializer;
|
private final ToApiJsonSerializer<Map<String, Object>> toApiJsonSerializer;
|
||||||
|
private final ToApiJsonSerializer<CommandProcessingResult> toApiResultJsonSerializer;
|
||||||
private CommandSourceRepository commandSourceRepository;
|
private CommandSourceRepository commandSourceRepository;
|
||||||
private final ConfigurationDomainService configurationDomainService;
|
private final ConfigurationDomainService configurationDomainService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public SynchronousCommandProcessingService(final PlatformSecurityContext context, final ApplicationContext applicationContext,
|
public SynchronousCommandProcessingService(final PlatformSecurityContext context, final ApplicationContext applicationContext,
|
||||||
final ToApiJsonSerializer<Map<String, Object>> toApiJsonSerializer, final CommandSourceRepository commandSourceRepository,
|
final ToApiJsonSerializer<Map<String, Object>> toApiJsonSerializer,
|
||||||
|
final ToApiJsonSerializer<CommandProcessingResult> toApiResultJsonSerializer,
|
||||||
|
final CommandSourceRepository commandSourceRepository,
|
||||||
final ConfigurationDomainService configurationDomainService) {
|
final ConfigurationDomainService configurationDomainService) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.applicationContext = applicationContext;
|
this.applicationContext = applicationContext;
|
||||||
this.toApiJsonSerializer = toApiJsonSerializer;
|
this.toApiJsonSerializer = toApiJsonSerializer;
|
||||||
|
this.toApiResultJsonSerializer = toApiResultJsonSerializer;
|
||||||
this.commandSourceRepository = commandSourceRepository;
|
this.commandSourceRepository = commandSourceRepository;
|
||||||
this.commandSourceRepository = commandSourceRepository;
|
this.commandSourceRepository = commandSourceRepository;
|
||||||
this.configurationDomainService = configurationDomainService;
|
this.configurationDomainService = configurationDomainService;
|
||||||
@@ -52,7 +59,7 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
|||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult processAndLogCommand(final CommandWrapper wrapper, final JsonCommand command,
|
public CommandProcessingResult processAndLogCommand(final CommandWrapper wrapper, final JsonCommand command,
|
||||||
final boolean isApprovedByChecker) {
|
final boolean isApprovedByChecker) {
|
||||||
|
|
||||||
final boolean rollbackTransaction = this.configurationDomainService.isMakerCheckerEnabledForTask(wrapper.taskPermissionName());
|
final boolean rollbackTransaction = this.configurationDomainService.isMakerCheckerEnabledForTask(wrapper.taskPermissionName());
|
||||||
|
|
||||||
final NewCommandSourceHandler handler = findCommandHandler(wrapper);
|
final NewCommandSourceHandler handler = findCommandHandler(wrapper);
|
||||||
@@ -101,6 +108,9 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
|||||||
throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(commandSourceResult);
|
throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(commandSourceResult);
|
||||||
}
|
}
|
||||||
result.setRollbackTransaction(null);
|
result.setRollbackTransaction(null);
|
||||||
|
|
||||||
|
publishEvent(wrapper.entityName(), wrapper.actionName(), result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,6 +233,16 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
|||||||
} else {
|
} else {
|
||||||
throw new UnsupportedCommandException(wrapper.commandName());
|
throw new UnsupportedCommandException(wrapper.commandName());
|
||||||
}
|
}
|
||||||
|
} else if (wrapper.isHookResource()) {
|
||||||
|
if (wrapper.isCreate()) {
|
||||||
|
handler = this.applicationContext.getBean("createHookCommandHandler", NewCommandSourceHandler.class);
|
||||||
|
} else if (wrapper.isUpdate()) {
|
||||||
|
handler = this.applicationContext.getBean("updateHookCommandHandler", NewCommandSourceHandler.class);
|
||||||
|
} else if (wrapper.isDelete()) {
|
||||||
|
handler = this.applicationContext.getBean("deleteHookCommandHandler", NewCommandSourceHandler.class);
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedCommandException(wrapper.commandName());
|
||||||
|
}
|
||||||
} else if (wrapper.isStaffResource()) {
|
} else if (wrapper.isStaffResource()) {
|
||||||
if (wrapper.isCreate()) {
|
if (wrapper.isCreate()) {
|
||||||
handler = this.applicationContext.getBean("createStaffCommandHandler", NewCommandSourceHandler.class);
|
handler = this.applicationContext.getBean("createStaffCommandHandler", NewCommandSourceHandler.class);
|
||||||
@@ -851,4 +871,20 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
|||||||
user.validateHasPermissionTo(commandWrapper.getTaskPermissionName());
|
user.validateHasPermissionTo(commandWrapper.getTaskPermissionName());
|
||||||
return rollbackTransaction;
|
return rollbackTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void publishEvent(final String entityName, final String actionName, final CommandProcessingResult result) {
|
||||||
|
|
||||||
|
final String authToken = ThreadLocalContextUtil.getAuthToken();
|
||||||
|
final String tenantIdentifier = ThreadLocalContextUtil.getTenant().getTenantIdentifier();
|
||||||
|
final AppUser appUser = this.context.authenticatedUser();
|
||||||
|
|
||||||
|
final HookEventSource hookEventSource = new HookEventSource(entityName, actionName);
|
||||||
|
|
||||||
|
final String serializedResult = this.toApiResultJsonSerializer.serialize(result);
|
||||||
|
|
||||||
|
final HookEvent applicationEvent =
|
||||||
|
new HookEvent(hookEventSource, serializedResult, tenantIdentifier, appUser, authToken);
|
||||||
|
|
||||||
|
applicationContext.publishEvent(applicationEvent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.infrastructure.core.data;
|
package org.mifosplatform.infrastructure.core.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -12,7 +13,7 @@ import java.util.Map;
|
|||||||
* Represents the successful result of an REST API call that results in
|
* Represents the successful result of an REST API call that results in
|
||||||
* processing a command.
|
* processing a command.
|
||||||
*/
|
*/
|
||||||
public class CommandProcessingResult {
|
public class CommandProcessingResult implements Serializable {
|
||||||
|
|
||||||
private Long commandId;
|
private Long commandId;
|
||||||
private Long officeId;
|
private Long officeId;
|
||||||
|
|||||||
+10
@@ -18,6 +18,8 @@ public class ThreadLocalContextUtil {
|
|||||||
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
|
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
|
||||||
|
|
||||||
private static final ThreadLocal<MifosPlatformTenant> tenantcontext = new ThreadLocal<>();
|
private static final ThreadLocal<MifosPlatformTenant> tenantcontext = new ThreadLocal<>();
|
||||||
|
|
||||||
|
private static final ThreadLocal<String> authTokenContext = new ThreadLocal<>();
|
||||||
|
|
||||||
public static void setTenant(final MifosPlatformTenant tenant) {
|
public static void setTenant(final MifosPlatformTenant tenant) {
|
||||||
Assert.notNull(tenant, "tenant cannot be null");
|
Assert.notNull(tenant, "tenant cannot be null");
|
||||||
@@ -43,5 +45,13 @@ public class ThreadLocalContextUtil {
|
|||||||
public static void clearDataSourceContext() {
|
public static void clearDataSourceContext() {
|
||||||
contextHolder.remove();
|
contextHolder.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setAuthToken(final String authToken) {
|
||||||
|
authTokenContext.set(authToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getAuthToken() {
|
||||||
|
return authTokenContext.get();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.api;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class HookApiConstants {
|
||||||
|
|
||||||
|
public static final String HOOK_RESOURCE_NAME = "HOOK";
|
||||||
|
|
||||||
|
public static final String nameParamName = "name";
|
||||||
|
|
||||||
|
public static final String displayNameParamName = "displayName";
|
||||||
|
|
||||||
|
public static final String isActiveParamName = "isActive";
|
||||||
|
|
||||||
|
public static final String webTemplateName = "Web";
|
||||||
|
|
||||||
|
public static final String smsTemplateName = "SMS Bridge";
|
||||||
|
|
||||||
|
public static final String payloadURLName = "Payload URL";
|
||||||
|
|
||||||
|
public static final String contentTypeName = "Content Type";
|
||||||
|
|
||||||
|
public static final String smsProviderName = "SMS Provider";
|
||||||
|
|
||||||
|
public static final String smsProviderAccountIdName = "SMS Provider Account Id";
|
||||||
|
|
||||||
|
public static final String smsProviderTokenIdName = "SMS Provider Token";
|
||||||
|
|
||||||
|
public static final String phoneNumberName = "Phone Number";
|
||||||
|
|
||||||
|
public static final String apiKeyName = "Api Key";
|
||||||
|
|
||||||
|
public static final String configParamName = "config";
|
||||||
|
|
||||||
|
public static final String eventsParamName = "events";
|
||||||
|
|
||||||
|
public static final String entityNameParamName = "entityName";
|
||||||
|
|
||||||
|
public static final String actionNameParamName = "actionName";
|
||||||
|
|
||||||
|
public static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(
|
||||||
|
Arrays.asList(nameParamName, displayNameParamName,
|
||||||
|
isActiveParamName, configParamName, eventsParamName));
|
||||||
|
|
||||||
|
public static final Set<String> UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<>(
|
||||||
|
Arrays.asList(nameParamName, displayNameParamName,
|
||||||
|
isActiveParamName, configParamName, eventsParamName));
|
||||||
|
|
||||||
|
}
|
||||||
+159
@@ -0,0 +1,159 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.api;
|
||||||
|
|
||||||
|
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.HOOK_RESOURCE_NAME;
|
||||||
|
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.RESPONSE_DATA_PARAMETERS;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.DELETE;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.PUT;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
|
||||||
|
import org.mifosplatform.commands.domain.CommandWrapper;
|
||||||
|
import org.mifosplatform.commands.service.CommandWrapperBuilder;
|
||||||
|
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||||
|
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
|
||||||
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||||
|
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||||
|
import org.mifosplatform.infrastructure.hooks.data.HookData;
|
||||||
|
import org.mifosplatform.infrastructure.hooks.service.HookReadPlatformService;
|
||||||
|
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Scope;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Path("/hooks")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Component
|
||||||
|
@Scope("singleton")
|
||||||
|
public class HookApiResource {
|
||||||
|
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
private final HookReadPlatformService readPlatformService;
|
||||||
|
private final DefaultToApiJsonSerializer<HookData> toApiJsonSerializer;
|
||||||
|
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||||
|
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public HookApiResource(
|
||||||
|
final PlatformSecurityContext context,
|
||||||
|
final HookReadPlatformService readPlatformService,
|
||||||
|
final DefaultToApiJsonSerializer<HookData> toApiJsonSerializer,
|
||||||
|
final ApiRequestParameterHelper apiRequestParameterHelper,
|
||||||
|
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
|
||||||
|
this.context = context;
|
||||||
|
this.readPlatformService = readPlatformService;
|
||||||
|
this.toApiJsonSerializer = toApiJsonSerializer;
|
||||||
|
this.apiRequestParameterHelper = apiRequestParameterHelper;
|
||||||
|
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public String retrieveHooks(@Context final UriInfo uriInfo) {
|
||||||
|
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(
|
||||||
|
HOOK_RESOURCE_NAME);
|
||||||
|
|
||||||
|
final Collection<HookData> hooks = this.readPlatformService
|
||||||
|
.retrieveAllHooks();
|
||||||
|
|
||||||
|
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper
|
||||||
|
.process(uriInfo.getQueryParameters());
|
||||||
|
return this.toApiJsonSerializer.serialize(settings, hooks,
|
||||||
|
RESPONSE_DATA_PARAMETERS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{hookId}")
|
||||||
|
public String retrieveHook(@PathParam("hookId") final Long hookId,
|
||||||
|
@Context final UriInfo uriInfo) {
|
||||||
|
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(
|
||||||
|
HOOK_RESOURCE_NAME);
|
||||||
|
|
||||||
|
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper
|
||||||
|
.process(uriInfo.getQueryParameters());
|
||||||
|
|
||||||
|
HookData hook = this.readPlatformService.retrieveHook(hookId);
|
||||||
|
|
||||||
|
if (settings.isTemplate()) {
|
||||||
|
final HookData hookData = this.readPlatformService
|
||||||
|
.retrieveNewHookDetails(hook.getTemplateName());
|
||||||
|
hook = HookData.templateExisting(hook, hookData.getTemplates(),
|
||||||
|
hookData.getGroupings());
|
||||||
|
}
|
||||||
|
return this.toApiJsonSerializer.serialize(settings, hook,
|
||||||
|
RESPONSE_DATA_PARAMETERS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("template")
|
||||||
|
public String template(@Context final UriInfo uriInfo) {
|
||||||
|
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(
|
||||||
|
HOOK_RESOURCE_NAME);
|
||||||
|
|
||||||
|
final HookData hook = this.readPlatformService
|
||||||
|
.retrieveNewHookDetails(null);
|
||||||
|
|
||||||
|
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper
|
||||||
|
.process(uriInfo.getQueryParameters());
|
||||||
|
return this.toApiJsonSerializer.serialize(settings, hook,
|
||||||
|
RESPONSE_DATA_PARAMETERS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
public String createHook(final String apiRequestBodyAsJson) {
|
||||||
|
|
||||||
|
final CommandWrapper commandRequest = new CommandWrapperBuilder()
|
||||||
|
.createHook().withJson(apiRequestBodyAsJson).build();
|
||||||
|
|
||||||
|
final CommandProcessingResult result = this.commandsSourceWritePlatformService
|
||||||
|
.logCommandSource(commandRequest);
|
||||||
|
|
||||||
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Path("{hookId}")
|
||||||
|
public String updateHook(@PathParam("hookId") final Long hookId,
|
||||||
|
final String apiRequestBodyAsJson) {
|
||||||
|
|
||||||
|
final CommandWrapper commandRequest = new CommandWrapperBuilder()
|
||||||
|
.updateHook(hookId).withJson(apiRequestBodyAsJson).build();
|
||||||
|
|
||||||
|
final CommandProcessingResult result = this.commandsSourceWritePlatformService
|
||||||
|
.logCommandSource(commandRequest);
|
||||||
|
|
||||||
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Path("{hookId}")
|
||||||
|
public String deleteHook(@PathParam("hookId") final Long hookId) {
|
||||||
|
|
||||||
|
final CommandWrapper commandRequest = new CommandWrapperBuilder()
|
||||||
|
.deleteHook(hookId).build();
|
||||||
|
|
||||||
|
final CommandProcessingResult result = this.commandsSourceWritePlatformService
|
||||||
|
.logCommandSource(commandRequest);
|
||||||
|
|
||||||
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class Entity implements Serializable {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private List<String> actions;
|
||||||
|
|
||||||
|
public void setName(final String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActions(final List<String> actions) {
|
||||||
|
this.actions = actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class Event implements Serializable {
|
||||||
|
|
||||||
|
private final String actionName;
|
||||||
|
private final String entityName;
|
||||||
|
|
||||||
|
public static Event instance(final String actionName,
|
||||||
|
final String entityName) {
|
||||||
|
return new Event(actionName, entityName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Event(final String actionName, final String entityName) {
|
||||||
|
this.actionName = actionName;
|
||||||
|
this.entityName = entityName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getActionName() {
|
||||||
|
return this.actionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEntityName() {
|
||||||
|
return this.entityName;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+96
@@ -0,0 +1,96 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.data;
|
||||||
|
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import org.springframework.dao.DataAccessException;
|
||||||
|
import org.springframework.jdbc.core.ResultSetExtractor;
|
||||||
|
|
||||||
|
public class EventResultSetExtractor implements
|
||||||
|
ResultSetExtractor<List<Grouping>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Grouping> extractData(final ResultSet rs) throws SQLException,
|
||||||
|
DataAccessException {
|
||||||
|
final List<Grouping> groupings = new ArrayList<>();
|
||||||
|
|
||||||
|
final Map<String, Map<String, List<String>>> groupToEntityMapping = new HashMap<>();
|
||||||
|
Map<String, List<String>> entityToActionMapping = new HashMap<>();
|
||||||
|
|
||||||
|
while (rs.next()) {
|
||||||
|
final String groupingName = rs.getString("grouping");
|
||||||
|
final String entityName = rs.getString("entity_name");
|
||||||
|
final String actionName = rs.getString("action_name");
|
||||||
|
Map<String, List<String>> entities = groupToEntityMapping
|
||||||
|
.get(groupingName);
|
||||||
|
List<String> actions = entityToActionMapping.get(entityName);
|
||||||
|
|
||||||
|
if (entities == null) {
|
||||||
|
entityToActionMapping = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actions == null) {
|
||||||
|
actions = new ArrayList<>();
|
||||||
|
}
|
||||||
|
actions.add(actionName);
|
||||||
|
entityToActionMapping.put(entityName, actions);
|
||||||
|
|
||||||
|
if (entities == null) {
|
||||||
|
entities = new HashMap<>();
|
||||||
|
}
|
||||||
|
entities.putAll(entityToActionMapping);
|
||||||
|
groupToEntityMapping.put(groupingName, entities);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final Entry<String, Map<String, List<String>>> groupingEntry : groupToEntityMapping
|
||||||
|
.entrySet()) {
|
||||||
|
final List<Entity> entities = new ArrayList<>();
|
||||||
|
final Grouping group = new Grouping();
|
||||||
|
group.setName(groupingEntry.getKey());
|
||||||
|
for (final Entry<String, List<String>> entityEntry : groupingEntry
|
||||||
|
.getValue().entrySet()) {
|
||||||
|
final List<String> actions = new ArrayList<>();
|
||||||
|
final Entity entity = new Entity();
|
||||||
|
entity.setName(entityEntry.getKey());
|
||||||
|
for (final String action : entityEntry.getValue()) {
|
||||||
|
actions.add(action);
|
||||||
|
}
|
||||||
|
Collections.sort(actions);
|
||||||
|
entity.setActions(actions);
|
||||||
|
entities.add(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(entities, new Comparator<Entity>() {
|
||||||
|
@Override
|
||||||
|
public int compare(final Entity entity1, final Entity entity2) {
|
||||||
|
return entity1.getName().compareTo(entity2.getName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
group.setEntities(entities);
|
||||||
|
groupings.add(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(groupings, new Comparator<Grouping>() {
|
||||||
|
@Override
|
||||||
|
public int compare(final Grouping grouping1,
|
||||||
|
final Grouping grouping2) {
|
||||||
|
return grouping1.getName().compareTo(grouping2.getName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return groupings;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.data;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class Field {
|
||||||
|
|
||||||
|
private final String fieldName;
|
||||||
|
private final String fieldValue;
|
||||||
|
private final String fieldType;
|
||||||
|
private final Boolean optional;
|
||||||
|
private final String placeholder;
|
||||||
|
|
||||||
|
public static Field fromConfig(final String fieldName,
|
||||||
|
final String fieldValue) {
|
||||||
|
return new Field(null, fieldName, fieldValue, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Field fromSchema(final String fieldType,
|
||||||
|
final String fieldName, final Boolean optional,
|
||||||
|
final String placeholder) {
|
||||||
|
return new Field(fieldType, fieldName, null, optional, placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Field(final String fieldType, final String fieldName,
|
||||||
|
final String fieldValue, final Boolean optional,
|
||||||
|
final String placeholder) {
|
||||||
|
this.fieldType = fieldType;
|
||||||
|
this.fieldName = fieldName;
|
||||||
|
this.fieldValue = fieldValue;
|
||||||
|
this.optional = optional;
|
||||||
|
this.placeholder = placeholder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFieldName() {
|
||||||
|
return this.fieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFieldValue() {
|
||||||
|
return this.fieldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFieldType() {
|
||||||
|
return this.fieldType;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class Grouping implements Serializable {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private List<Entity> entities;
|
||||||
|
|
||||||
|
public void setName(final String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEntities(final List<Entity> entities) {
|
||||||
|
this.entities = entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+91
@@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.joda.time.LocalDate;
|
||||||
|
|
||||||
|
public class HookData implements Serializable {
|
||||||
|
|
||||||
|
private final Long id;
|
||||||
|
private final String name;
|
||||||
|
private final String displayName;
|
||||||
|
private final Boolean isActive;
|
||||||
|
private final LocalDate createdAt;
|
||||||
|
private final LocalDate updatedAt;
|
||||||
|
|
||||||
|
// associations
|
||||||
|
private final List<Event> events;
|
||||||
|
private final List<Field> config;
|
||||||
|
|
||||||
|
// template data
|
||||||
|
private final List<HookTemplateData> templates;
|
||||||
|
private final List<Grouping> groupings;
|
||||||
|
|
||||||
|
public static HookData instance(final Long id, final String name,
|
||||||
|
final String displayName, final boolean isActive,
|
||||||
|
final LocalDate createdAt, final LocalDate updatedAt,
|
||||||
|
final List<Event> registeredEvents, final List<Field> config) {
|
||||||
|
return new HookData(id, name, displayName, isActive, createdAt,
|
||||||
|
updatedAt, registeredEvents, config, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HookData template(final List<HookTemplateData> templates,
|
||||||
|
final List<Grouping> groupings) {
|
||||||
|
return new HookData(null, null, null, null, null, null, null, null,
|
||||||
|
templates, groupings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HookData templateExisting(final HookData hookData,
|
||||||
|
final List<HookTemplateData> templates,
|
||||||
|
final List<Grouping> groupings) {
|
||||||
|
return new HookData(hookData.id, hookData.name, hookData.displayName,
|
||||||
|
hookData.isActive, hookData.createdAt, hookData.updatedAt,
|
||||||
|
hookData.events, hookData.config, templates, groupings);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HookData(final Long id, final String name,
|
||||||
|
final String displayName, final Boolean isActive,
|
||||||
|
final LocalDate createdAt, final LocalDate updatedAt,
|
||||||
|
final List<Event> events, final List<Field> config,
|
||||||
|
final List<HookTemplateData> templates,
|
||||||
|
final List<Grouping> groupings) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this.displayName = displayName;
|
||||||
|
this.isActive = isActive;
|
||||||
|
this.createdAt = createdAt;
|
||||||
|
this.updatedAt = updatedAt;
|
||||||
|
|
||||||
|
// associations
|
||||||
|
this.events = events;
|
||||||
|
this.config = config;
|
||||||
|
|
||||||
|
// template
|
||||||
|
this.templates = templates;
|
||||||
|
this.groupings = groupings;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getHookId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<HookTemplateData> getTemplates() {
|
||||||
|
return this.templates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Grouping> getGroupings() {
|
||||||
|
return this.groupings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTemplateName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class HookTemplateData implements Serializable {
|
||||||
|
|
||||||
|
private final Long id;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
// associations
|
||||||
|
private final List<Field> schema;
|
||||||
|
|
||||||
|
public static HookTemplateData instance(final Long id, final String name,
|
||||||
|
final List<Field> schema) {
|
||||||
|
return new HookTemplateData(id, name, schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HookTemplateData(final Long id, final String name,
|
||||||
|
final List<Field> schema) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
|
||||||
|
// associations
|
||||||
|
this.schema = schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getServiceId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+185
@@ -0,0 +1,185 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.domain;
|
||||||
|
|
||||||
|
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.configParamName;
|
||||||
|
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.displayNameParamName;
|
||||||
|
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.hibernate.annotations.LazyCollection;
|
||||||
|
import org.hibernate.annotations.LazyCollectionOption;
|
||||||
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom;
|
||||||
|
import org.mifosplatform.useradministration.domain.AppUser;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_hook")
|
||||||
|
public class Hook extends AbstractAuditableCustom<AppUser, Long> {
|
||||||
|
|
||||||
|
@Column(name = "name", nullable = false, length = 100)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(name = "is_active", nullable = false)
|
||||||
|
private Boolean isActive;
|
||||||
|
|
||||||
|
@LazyCollection(LazyCollectionOption.FALSE)
|
||||||
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "hook", orphanRemoval = true)
|
||||||
|
private Set<HookResource> events = new HashSet<>();
|
||||||
|
|
||||||
|
@LazyCollection(LazyCollectionOption.FALSE)
|
||||||
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "hook", orphanRemoval = true)
|
||||||
|
private Set<HookConfiguration> config = new HashSet<>();
|
||||||
|
|
||||||
|
@ManyToOne(optional = true)
|
||||||
|
@JoinColumn(name = "template_id", referencedColumnName = "id", nullable = false)
|
||||||
|
private HookTemplate template;
|
||||||
|
|
||||||
|
protected Hook() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Hook fromJson(final JsonCommand command,
|
||||||
|
final HookTemplate template, final Set<HookConfiguration> config,
|
||||||
|
final Set<HookResource> events) {
|
||||||
|
final String displayName = command
|
||||||
|
.stringValueOfParameterNamed(displayNameParamName);
|
||||||
|
final Boolean isActive = command
|
||||||
|
.booleanObjectValueOfParameterNamed(isActiveParamName);
|
||||||
|
return new Hook(template, displayName, isActive, config, events);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Hook(final HookTemplate template, final String displayName,
|
||||||
|
final Boolean isActive, final Set<HookConfiguration> config,
|
||||||
|
final Set<HookResource> events) {
|
||||||
|
|
||||||
|
this.template = template;
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(displayName)) {
|
||||||
|
this.name = displayName.trim();
|
||||||
|
} else {
|
||||||
|
this.name = template.getName();
|
||||||
|
}
|
||||||
|
this.isActive = isActive;
|
||||||
|
if (!CollectionUtils.isEmpty(config)) {
|
||||||
|
this.config = associateConfigWithThisHook(config);
|
||||||
|
}
|
||||||
|
if (!CollectionUtils.isEmpty(events)) {
|
||||||
|
this.events = associateEventsWithThisHook(events);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<HookConfiguration> associateConfigWithThisHook(
|
||||||
|
final Set<HookConfiguration> config) {
|
||||||
|
for (final HookConfiguration hookConfiguration : config) {
|
||||||
|
hookConfiguration.update(this);
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<HookResource> associateEventsWithThisHook(
|
||||||
|
final Set<HookResource> events) {
|
||||||
|
for (final HookResource hookResource : events) {
|
||||||
|
hookResource.update(this);
|
||||||
|
}
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HookTemplate getHookTemplate() {
|
||||||
|
return this.template;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<HookConfiguration> getHookConfig() {
|
||||||
|
return this.config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> update(final JsonCommand command) {
|
||||||
|
|
||||||
|
final Map<String, Object> actualChanges = new LinkedHashMap<>(4);
|
||||||
|
|
||||||
|
if (command.isChangeInStringParameterNamed(displayNameParamName,
|
||||||
|
this.name)) {
|
||||||
|
final String newValue = command
|
||||||
|
.stringValueOfParameterNamed(displayNameParamName);
|
||||||
|
actualChanges.put(displayNameParamName, newValue);
|
||||||
|
this.name = newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.isChangeInBooleanParameterNamed(isActiveParamName,
|
||||||
|
this.isActive)) {
|
||||||
|
final Boolean newValue = command
|
||||||
|
.booleanObjectValueOfParameterNamed(isActiveParamName);
|
||||||
|
actualChanges.put(isActiveParamName, newValue);
|
||||||
|
this.isActive = newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// events
|
||||||
|
if (command.hasParameter(eventsParamName)) {
|
||||||
|
final JsonArray jsonArray = command
|
||||||
|
.arrayOfParameterNamed(eventsParamName);
|
||||||
|
if (jsonArray != null) {
|
||||||
|
actualChanges.put(eventsParamName, jsonArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// config
|
||||||
|
if (command.hasParameter(configParamName)) {
|
||||||
|
final JsonElement element = command.parsedJson().getAsJsonObject()
|
||||||
|
.get(configParamName);
|
||||||
|
if (element != null) {
|
||||||
|
actualChanges.put(configParamName, element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return actualChanges;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean updateEvents(final Set<HookResource> newHookEvents) {
|
||||||
|
if (newHookEvents == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.events == null) {
|
||||||
|
this.events = new HashSet<>();
|
||||||
|
}
|
||||||
|
this.events.clear();
|
||||||
|
this.events.addAll(associateEventsWithThisHook(newHookEvents));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean updateConfig(final Set<HookConfiguration> newHookConfig) {
|
||||||
|
if (newHookConfig == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.config == null) {
|
||||||
|
this.config = new HashSet<>();
|
||||||
|
}
|
||||||
|
this.config.clear();
|
||||||
|
this.config.addAll(associateConfigWithThisHook(newHookConfig));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+72
@@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.domain;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_hook_configuration")
|
||||||
|
public class HookConfiguration extends AbstractPersistable<Long> {
|
||||||
|
|
||||||
|
@ManyToOne(optional = false)
|
||||||
|
@JoinColumn(name = "hook_id", referencedColumnName = "id", nullable = false)
|
||||||
|
private Hook hook;
|
||||||
|
|
||||||
|
@Column(name = "field_type", nullable = false, length = 20)
|
||||||
|
private String fieldType;
|
||||||
|
|
||||||
|
@Column(name = "field_name", nullable = false, length = 100)
|
||||||
|
private String fieldName;
|
||||||
|
|
||||||
|
@Column(name = "field_value", nullable = false, length = 100)
|
||||||
|
private String fieldValue;
|
||||||
|
|
||||||
|
public static HookConfiguration createNewWithoutHook(
|
||||||
|
final String fieldType, final String fieldName,
|
||||||
|
final String fieldValue) {
|
||||||
|
return new HookConfiguration(null, fieldType, fieldName, fieldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HookConfiguration createNew(final Hook hook, final String fieldType,
|
||||||
|
final String fieldName, final String fieldValue) {
|
||||||
|
return new HookConfiguration(hook, fieldType, fieldName, fieldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected HookConfiguration() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
private HookConfiguration(final Hook hook, final String fieldType,
|
||||||
|
final String fieldName, final String fieldValue) {
|
||||||
|
this.hook = hook;
|
||||||
|
this.fieldType = fieldType;
|
||||||
|
this.fieldName = fieldName;
|
||||||
|
this.fieldValue = fieldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFieldName() {
|
||||||
|
return this.fieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFieldType() {
|
||||||
|
return this.fieldType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFieldValue() {
|
||||||
|
return this.fieldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(final Hook hook) {
|
||||||
|
this.hook = hook;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.domain;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
public interface HookConfigurationRepository extends
|
||||||
|
JpaRepository<HookConfiguration, Long>,
|
||||||
|
JpaSpecificationExecutor<HookConfiguration> {
|
||||||
|
|
||||||
|
@Query("select config.fieldValue from HookConfiguration config where config.hook.id = :hookId and config.fieldName = :fieldName")
|
||||||
|
String findOneByHookIdAndFieldName(@Param("hookId") Long hookId,
|
||||||
|
@Param("fieldName") String fieldName);
|
||||||
|
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.domain;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
public interface HookRepository extends JpaRepository<Hook, Long>,
|
||||||
|
JpaSpecificationExecutor<Hook> {
|
||||||
|
|
||||||
|
@Query("select hook from Hook hook inner join hook.events event where event.entityName = :entityName and event.actionName = :actionName and hook.isActive = 1")
|
||||||
|
List<Hook> findAllHooksListeningToEvent(
|
||||||
|
@Param("entityName") String entityName,
|
||||||
|
@Param("actionName") String actionName);
|
||||||
|
|
||||||
|
@Query("from Hook hook where hook.template.id = :templateId ")
|
||||||
|
Hook findOneByTemplateId(@Param("templateId") Long templateId);
|
||||||
|
|
||||||
|
}
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.domain;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_hook_registered_events")
|
||||||
|
public class HookResource extends AbstractPersistable<Long> {
|
||||||
|
|
||||||
|
@ManyToOne(optional = false)
|
||||||
|
@JoinColumn(name = "hook_id", referencedColumnName = "id", nullable = false)
|
||||||
|
private Hook hook;
|
||||||
|
|
||||||
|
@Column(name = "entity_name", nullable = false, length = 45)
|
||||||
|
private String entityName;
|
||||||
|
|
||||||
|
@Column(name = "action_name", nullable = false, length = 45)
|
||||||
|
private String actionName;
|
||||||
|
|
||||||
|
protected HookResource() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HookResource createNewWithoutHook(final String entityName,
|
||||||
|
final String actionName) {
|
||||||
|
return new HookResource(null, entityName, actionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HookResource(final Hook hook, final String entityName,
|
||||||
|
final String actionName) {
|
||||||
|
this.hook = hook;
|
||||||
|
this.entityName = entityName;
|
||||||
|
this.actionName = actionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(final Hook hook) {
|
||||||
|
this.hook = hook;
|
||||||
|
}
|
||||||
|
}
|
||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.domain;
|
||||||
|
|
||||||
|
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.nameParamName;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.hibernate.annotations.LazyCollection;
|
||||||
|
import org.hibernate.annotations.LazyCollectionOption;
|
||||||
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_hook_templates")
|
||||||
|
public class HookTemplate extends AbstractPersistable<Long> {
|
||||||
|
|
||||||
|
@Column(name = "name", nullable = false, length = 100)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@LazyCollection(LazyCollectionOption.FALSE)
|
||||||
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "template", orphanRemoval = true)
|
||||||
|
private final Set<Schema> fields = new HashSet<>();
|
||||||
|
|
||||||
|
private HookTemplate(final String name) {
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(name)) {
|
||||||
|
this.name = name.trim();
|
||||||
|
} else {
|
||||||
|
this.name = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected HookTemplate() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HookTemplate fromJson(final JsonCommand command) {
|
||||||
|
final String name = command.stringValueOfParameterNamed(nameParamName);
|
||||||
|
return new HookTemplate(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Schema> getSchema() {
|
||||||
|
return this.fields;
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.domain;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
public interface HookTemplateRepository extends
|
||||||
|
JpaRepository<HookTemplate, Long>,
|
||||||
|
JpaSpecificationExecutor<HookTemplate> {
|
||||||
|
|
||||||
|
@Query("from HookTemplate template where template.name = :name")
|
||||||
|
HookTemplate findOne(@Param("name") String name);
|
||||||
|
}
|
||||||
+52
@@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.domain;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_hook_schema")
|
||||||
|
public class Schema extends AbstractPersistable<Long> {
|
||||||
|
|
||||||
|
@ManyToOne(optional = false)
|
||||||
|
@JoinColumn(name = "hook_template_id", referencedColumnName = "id", nullable = false)
|
||||||
|
private HookTemplate template;
|
||||||
|
|
||||||
|
@Column(name = "field_type", nullable = false, length = 20)
|
||||||
|
private String fieldType;
|
||||||
|
|
||||||
|
@Column(name = "field_name", nullable = false, length = 100)
|
||||||
|
private String fieldName;
|
||||||
|
|
||||||
|
@Column(name = "placeholder", length = 100)
|
||||||
|
private String placeholder;
|
||||||
|
|
||||||
|
@Column(name = "optional", nullable = false)
|
||||||
|
private final boolean optional = false;
|
||||||
|
|
||||||
|
public Schema() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFieldName() {
|
||||||
|
return this.fieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFieldType() {
|
||||||
|
return this.fieldType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOptional() {
|
||||||
|
return this.optional;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+52
@@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.event;
|
||||||
|
|
||||||
|
import org.mifosplatform.useradministration.domain.AppUser;
|
||||||
|
import org.springframework.context.ApplicationEvent;
|
||||||
|
|
||||||
|
public class HookEvent extends ApplicationEvent {
|
||||||
|
|
||||||
|
private final String payload;
|
||||||
|
|
||||||
|
private final String tenantIdentifier;
|
||||||
|
|
||||||
|
private final AppUser appUser;
|
||||||
|
|
||||||
|
private final String authToken;
|
||||||
|
|
||||||
|
public HookEvent(final HookEventSource source, final String payload,
|
||||||
|
final String tenantIdentifier, final AppUser appUser,
|
||||||
|
final String authToken) {
|
||||||
|
super(source);
|
||||||
|
this.payload = payload;
|
||||||
|
this.tenantIdentifier = tenantIdentifier;
|
||||||
|
this.appUser = appUser;
|
||||||
|
this.authToken = authToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPayload() {
|
||||||
|
return this.payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HookEventSource getSource() {
|
||||||
|
return (HookEventSource) super.source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTenantIdentifier() {
|
||||||
|
return this.tenantIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppUser getAppUser() {
|
||||||
|
return this.appUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthToken() {
|
||||||
|
return this.authToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.event;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class HookEventSource implements Serializable {
|
||||||
|
|
||||||
|
private final String entityName;
|
||||||
|
|
||||||
|
private final String actionName;
|
||||||
|
|
||||||
|
public HookEventSource(final String entityName, final String actionName) {
|
||||||
|
super();
|
||||||
|
this.entityName = entityName;
|
||||||
|
this.actionName = actionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEntityName() {
|
||||||
|
return this.entityName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getActionName() {
|
||||||
|
return this.actionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.exception;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||||
|
|
||||||
|
public class HookNotFoundException extends
|
||||||
|
AbstractPlatformResourceNotFoundException {
|
||||||
|
|
||||||
|
public HookNotFoundException(final String name) {
|
||||||
|
super("error.msg.hook.not.found", "Hook with name `" + name
|
||||||
|
+ "` does not exist", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HookNotFoundException(final Long hookId) {
|
||||||
|
super("error.msg.hook.identifier.not.found", "Hook with identifier `"
|
||||||
|
+ hookId + "` does not exist", hookId);
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.exception;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||||
|
|
||||||
|
public class HookTemplateNotFoundException extends
|
||||||
|
AbstractPlatformResourceNotFoundException {
|
||||||
|
|
||||||
|
public HookTemplateNotFoundException(final String name) {
|
||||||
|
super("error.msg.template.not.found", "Template with name `" + name
|
||||||
|
+ "` does not exist", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HookTemplateNotFoundException(final Long templateId) {
|
||||||
|
super("error.msg.template.identifier.not.found",
|
||||||
|
"Template with identifier `" + templateId + "` does not exist",
|
||||||
|
templateId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.handler;
|
||||||
|
|
||||||
|
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||||
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.mifosplatform.infrastructure.hooks.service.HookWritePlatformService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class CreateHookCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final HookWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public CreateHookCommandHandler(
|
||||||
|
final HookWritePlatformService writePlatformService) {
|
||||||
|
this.writePlatformService = writePlatformService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
|
||||||
|
return this.writePlatformService.createHook(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.handler;
|
||||||
|
|
||||||
|
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||||
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.mifosplatform.infrastructure.hooks.service.HookWritePlatformService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class DeleteHookCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final HookWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public DeleteHookCommandHandler(
|
||||||
|
final HookWritePlatformService writePlatformService) {
|
||||||
|
this.writePlatformService = writePlatformService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
|
||||||
|
return this.writePlatformService.deleteHook(command.entityId());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.handler;
|
||||||
|
|
||||||
|
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||||
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.mifosplatform.infrastructure.hooks.service.HookWritePlatformService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class UpdateHookCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final HookWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public UpdateHookCommandHandler(
|
||||||
|
final HookWritePlatformService writePlatformService) {
|
||||||
|
this.writePlatformService = writePlatformService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
|
||||||
|
return this.writePlatformService
|
||||||
|
.updateHook(command.entityId(), command);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.listener;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.hooks.event.HookEvent;
|
||||||
|
import org.springframework.context.ApplicationListener;
|
||||||
|
|
||||||
|
public interface HookListener extends ApplicationListener<HookEvent> {
|
||||||
|
|
||||||
|
}
|
||||||
+66
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.listener;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenant;
|
||||||
|
import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil;
|
||||||
|
import org.mifosplatform.infrastructure.hooks.domain.Hook;
|
||||||
|
import org.mifosplatform.infrastructure.hooks.domain.HookRepository;
|
||||||
|
import org.mifosplatform.infrastructure.hooks.event.HookEvent;
|
||||||
|
import org.mifosplatform.infrastructure.hooks.event.HookEventSource;
|
||||||
|
import org.mifosplatform.infrastructure.hooks.processor.HookProcessor;
|
||||||
|
import org.mifosplatform.infrastructure.hooks.processor.HookProcessorProvider;
|
||||||
|
import org.mifosplatform.infrastructure.security.service.TenantDetailsService;
|
||||||
|
import org.mifosplatform.useradministration.domain.AppUser;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class MifosHookListener implements HookListener {
|
||||||
|
|
||||||
|
private final HookProcessorProvider hookProcessorProvider;
|
||||||
|
private final HookRepository hookRepository;
|
||||||
|
private final TenantDetailsService tenantDetailsService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public MifosHookListener(final HookProcessorProvider hookProcessorProvider,
|
||||||
|
final HookRepository hookRepository,
|
||||||
|
final TenantDetailsService tenantDetailsService) {
|
||||||
|
this.hookRepository = hookRepository;
|
||||||
|
this.hookProcessorProvider = hookProcessorProvider;
|
||||||
|
this.tenantDetailsService = tenantDetailsService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApplicationEvent(final HookEvent event) {
|
||||||
|
|
||||||
|
final String tenantIdentifier = event.getTenantIdentifier();
|
||||||
|
final MifosPlatformTenant tenant = this.tenantDetailsService
|
||||||
|
.loadTenantById(tenantIdentifier);
|
||||||
|
ThreadLocalContextUtil.setTenant(tenant);
|
||||||
|
|
||||||
|
final AppUser appUser = event.getAppUser();
|
||||||
|
final String authToken = event.getAuthToken();
|
||||||
|
|
||||||
|
final HookEventSource hookEventSource = event.getSource();
|
||||||
|
final String entityName = hookEventSource.getEntityName();
|
||||||
|
final String actionName = hookEventSource.getActionName();
|
||||||
|
final String payload = event.getPayload();
|
||||||
|
|
||||||
|
final List<Hook> hooks = this.hookRepository
|
||||||
|
.findAllHooksListeningToEvent(hookEventSource.getEntityName(),
|
||||||
|
hookEventSource.getActionName());
|
||||||
|
for (final Hook hook : hooks) {
|
||||||
|
final HookProcessor processor = this.hookProcessorProvider
|
||||||
|
.getProcessor(hook);
|
||||||
|
processor.process(hook, appUser, payload, entityName, actionName,
|
||||||
|
tenantIdentifier, authToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.processor;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.hooks.domain.Hook;
|
||||||
|
import org.mifosplatform.useradministration.domain.AppUser;
|
||||||
|
|
||||||
|
public interface HookProcessor {
|
||||||
|
|
||||||
|
void process(Hook hook, AppUser appUser, String payload, String entityName,
|
||||||
|
String actionName, String tenantIdentifier, String authToken);
|
||||||
|
|
||||||
|
}
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.processor;
|
||||||
|
|
||||||
|
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.smsTemplateName;
|
||||||
|
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.webTemplateName;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.hooks.domain.Hook;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class HookProcessorProvider implements ApplicationContextAware {
|
||||||
|
|
||||||
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApplicationContext(
|
||||||
|
final ApplicationContext applicationContext) throws BeansException {
|
||||||
|
this.applicationContext = applicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HookProcessor getProcessor(final Hook hook) {
|
||||||
|
HookProcessor processor;
|
||||||
|
final String templateName = hook.getHookTemplate().getName();
|
||||||
|
if (templateName.equalsIgnoreCase(smsTemplateName)) {
|
||||||
|
processor = this.applicationContext.getBean("twilioHookProcessor",
|
||||||
|
TwilioHookProcessor.class);
|
||||||
|
} else if (templateName.equals(webTemplateName)) {
|
||||||
|
processor = this.applicationContext.getBean("webHookProcessor",
|
||||||
|
WebHookProcessor.class);
|
||||||
|
} else {
|
||||||
|
processor = null;
|
||||||
|
}
|
||||||
|
return processor;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+110
@@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.processor;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
|
import javax.net.ssl.HostnameVerifier;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLSession;
|
||||||
|
import javax.net.ssl.TrustManager;
|
||||||
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import retrofit.Callback;
|
||||||
|
import retrofit.RestAdapter;
|
||||||
|
import retrofit.RetrofitError;
|
||||||
|
import retrofit.client.OkClient;
|
||||||
|
import retrofit.client.Response;
|
||||||
|
|
||||||
|
import com.squareup.okhttp.OkHttpClient;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class ProcessorHelper {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory
|
||||||
|
.getLogger(ProcessorHelper.class);
|
||||||
|
|
||||||
|
@SuppressWarnings("null")
|
||||||
|
public static OkHttpClient configureClient(final OkHttpClient client) {
|
||||||
|
final TrustManager[] certs = new TrustManager[] { new X509TrustManager() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public X509Certificate[] getAcceptedIssuers() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkServerTrusted(final X509Certificate[] chain,
|
||||||
|
final String authType) throws CertificateException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkClientTrusted(final X509Certificate[] chain,
|
||||||
|
final String authType) throws CertificateException {
|
||||||
|
}
|
||||||
|
} };
|
||||||
|
|
||||||
|
SSLContext ctx = null;
|
||||||
|
try {
|
||||||
|
ctx = SSLContext.getInstance("TLS");
|
||||||
|
ctx.init(null, certs, new SecureRandom());
|
||||||
|
} catch (final java.security.GeneralSecurityException ex) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final HostnameVerifier hostnameVerifier = new HostnameVerifier() {
|
||||||
|
@Override
|
||||||
|
public boolean verify(final String hostname,
|
||||||
|
final SSLSession session) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
client.setHostnameVerifier(hostnameVerifier);
|
||||||
|
client.setSslSocketFactory(ctx.getSocketFactory());
|
||||||
|
} catch (final Exception e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OkHttpClient createClient() {
|
||||||
|
final OkHttpClient client = new OkHttpClient();
|
||||||
|
return configureClient(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public static Callback createCallback(final String url) {
|
||||||
|
|
||||||
|
return new Callback() {
|
||||||
|
@Override
|
||||||
|
public void success(final Object o, final Response response) {
|
||||||
|
logger.info("URL : " + url + "\tStatus : "
|
||||||
|
+ response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failure(final RetrofitError retrofitError) {
|
||||||
|
logger.info(retrofitError.getMessage());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WebHookService createWebHookService(final String url) {
|
||||||
|
|
||||||
|
final OkHttpClient client = ProcessorHelper.createClient();
|
||||||
|
|
||||||
|
final RestAdapter restAdapter = new RestAdapter.Builder()
|
||||||
|
.setEndpoint(url).setClient(new OkClient(client)).build();
|
||||||
|
|
||||||
|
return restAdapter.create(WebHookService.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+83
@@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.processor;
|
||||||
|
|
||||||
|
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.apiKeyName;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.hooks.domain.Hook;
|
||||||
|
import org.mifosplatform.infrastructure.hooks.domain.HookConfiguration;
|
||||||
|
import org.mifosplatform.infrastructure.hooks.domain.HookConfigurationRepository;
|
||||||
|
import org.mifosplatform.infrastructure.hooks.processor.data.SmsProviderData;
|
||||||
|
import org.mifosplatform.useradministration.domain.AppUser;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import retrofit.Callback;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class TwilioHookProcessor implements HookProcessor {
|
||||||
|
|
||||||
|
private final HookConfigurationRepository hookConfigurationRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public TwilioHookProcessor(
|
||||||
|
final HookConfigurationRepository hookConfigurationRepository) {
|
||||||
|
this.hookConfigurationRepository = hookConfigurationRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(final Hook hook,
|
||||||
|
@SuppressWarnings("unused") final AppUser appUser,
|
||||||
|
final String payload, final String entityName,
|
||||||
|
final String actionName, final String tenantIdentifier,
|
||||||
|
final String authToken) {
|
||||||
|
|
||||||
|
final SmsProviderData smsProviderData = new SmsProviderData(
|
||||||
|
hook.getHookConfig());
|
||||||
|
|
||||||
|
sendRequest(smsProviderData, payload, entityName, actionName,
|
||||||
|
tenantIdentifier, authToken, hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void sendRequest(final SmsProviderData smsProviderData,
|
||||||
|
final String payload, final String entityName,
|
||||||
|
final String actionName, final String tenantIdentifier,
|
||||||
|
final String authToken, final Hook hook) {
|
||||||
|
|
||||||
|
final WebHookService service = ProcessorHelper
|
||||||
|
.createWebHookService(smsProviderData.getUrl());
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
final Callback callback = ProcessorHelper
|
||||||
|
.createCallback(smsProviderData.getUrl());
|
||||||
|
|
||||||
|
String apiKey = this.hookConfigurationRepository
|
||||||
|
.findOneByHookIdAndFieldName(hook.getId(), apiKeyName);
|
||||||
|
if (apiKey == null) {
|
||||||
|
smsProviderData.setUrl(null);
|
||||||
|
smsProviderData.setEndpoint(System.getProperty("baseUrl"));
|
||||||
|
smsProviderData.setTenantId(tenantIdentifier);
|
||||||
|
smsProviderData.setMifosToken(authToken);
|
||||||
|
apiKey = service.sendSmsBridgeConfigRequest(smsProviderData);
|
||||||
|
final HookConfiguration apiKeyEntry = HookConfiguration.createNew(
|
||||||
|
hook, "string", apiKeyName, apiKey);
|
||||||
|
this.hookConfigurationRepository.save(apiKeyEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (apiKey != null && !apiKey.equals("")) {
|
||||||
|
final JsonObject json = new JsonParser().parse(payload)
|
||||||
|
.getAsJsonObject();
|
||||||
|
service.sendSmsBridgeRequest(entityName, actionName,
|
||||||
|
tenantIdentifier, apiKey, json, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+86
@@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.processor;
|
||||||
|
|
||||||
|
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.contentTypeName;
|
||||||
|
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.payloadURLName;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.hooks.domain.Hook;
|
||||||
|
import org.mifosplatform.infrastructure.hooks.domain.HookConfiguration;
|
||||||
|
import org.mifosplatform.useradministration.domain.AppUser;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import retrofit.Callback;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class WebHookProcessor implements HookProcessor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(final Hook hook,
|
||||||
|
@SuppressWarnings("unused") final AppUser appUser,
|
||||||
|
final String payload, final String entityName,
|
||||||
|
final String actionName, final String tenantIdentifier,
|
||||||
|
final String authToken) {
|
||||||
|
|
||||||
|
final Set<HookConfiguration> config = hook.getHookConfig();
|
||||||
|
|
||||||
|
String url = "";
|
||||||
|
String contentType = "";
|
||||||
|
|
||||||
|
for (final HookConfiguration conf : config) {
|
||||||
|
final String fieldName = conf.getFieldName();
|
||||||
|
if (fieldName.equals(payloadURLName)) {
|
||||||
|
url = conf.getFieldValue();
|
||||||
|
}
|
||||||
|
if (fieldName.equals(contentTypeName)) {
|
||||||
|
contentType = conf.getFieldValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendRequest(url, contentType, payload, entityName, actionName,
|
||||||
|
tenantIdentifier, authToken);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void sendRequest(final String url, final String contentType,
|
||||||
|
final String payload, final String entityName,
|
||||||
|
final String actionName, final String tenantIdentifier,
|
||||||
|
final String authToken) {
|
||||||
|
|
||||||
|
final String mifosEndpointUrl = System.getProperty("baseUrl");
|
||||||
|
final WebHookService service = ProcessorHelper
|
||||||
|
.createWebHookService(url);
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
final Callback callback = ProcessorHelper.createCallback(url);
|
||||||
|
|
||||||
|
if (contentType.equalsIgnoreCase("json")
|
||||||
|
|| contentType.contains("json")) {
|
||||||
|
final JsonObject json = new JsonParser().parse(payload)
|
||||||
|
.getAsJsonObject();
|
||||||
|
json.addProperty("mifosToken", authToken);
|
||||||
|
service.sendJsonRequest(entityName, actionName, tenantIdentifier,
|
||||||
|
mifosEndpointUrl, json, callback);
|
||||||
|
} else {
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
map = new Gson().fromJson(payload, map.getClass());
|
||||||
|
map.put("mifosToken", authToken);
|
||||||
|
service.sendFormRequest(entityName, actionName, tenantIdentifier,
|
||||||
|
mifosEndpointUrl, map, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.processor;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.hooks.processor.data.SmsProviderData;
|
||||||
|
|
||||||
|
import retrofit.Callback;
|
||||||
|
import retrofit.client.Response;
|
||||||
|
import retrofit.http.Body;
|
||||||
|
import retrofit.http.FieldMap;
|
||||||
|
import retrofit.http.FormUrlEncoded;
|
||||||
|
import retrofit.http.Header;
|
||||||
|
import retrofit.http.POST;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
public interface WebHookService {
|
||||||
|
|
||||||
|
final static String ENTITY_HEADER = "X-Mifos-Entity";
|
||||||
|
final static String ACTION_HEADER = "X-Mifos-Action";
|
||||||
|
final static String TENANT_HEADER = "X-Mifos-Platform-TenantId";
|
||||||
|
final static String ENDPOINT_HEADER = "X-Mifos-Endpoint";
|
||||||
|
final static String API_KEY_HEADER = "X-Mifos-API-Key";
|
||||||
|
|
||||||
|
// Template - Web
|
||||||
|
@POST("/")
|
||||||
|
void sendJsonRequest(@Header(ENTITY_HEADER) String entityHeader,
|
||||||
|
@Header(ACTION_HEADER) String actionHeader,
|
||||||
|
@Header(TENANT_HEADER) String tenantHeader,
|
||||||
|
@Header(ENDPOINT_HEADER) String endpointHeader,
|
||||||
|
@Body JsonObject result, Callback<Response> callBack);
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("/")
|
||||||
|
void sendFormRequest(@Header(ENTITY_HEADER) String entityHeader,
|
||||||
|
@Header(ACTION_HEADER) String actionHeader,
|
||||||
|
@Header(TENANT_HEADER) String tenantHeader,
|
||||||
|
@Header(ENDPOINT_HEADER) String endpointHeader,
|
||||||
|
@FieldMap Map<String, String> params, Callback<Response> callBack);
|
||||||
|
|
||||||
|
// Template - SMS Bridge
|
||||||
|
@POST("/")
|
||||||
|
void sendSmsBridgeRequest(@Header(ENTITY_HEADER) String entityHeader,
|
||||||
|
@Header(ACTION_HEADER) String actionHeader,
|
||||||
|
@Header(TENANT_HEADER) String tenantHeader,
|
||||||
|
@Header(API_KEY_HEADER) String apiKeyHeader,
|
||||||
|
@Body JsonObject result, Callback<Response> callBack);
|
||||||
|
|
||||||
|
@POST("/configuration")
|
||||||
|
String sendSmsBridgeConfigRequest(@Body SmsProviderData config);
|
||||||
|
|
||||||
|
}
|
||||||
+99
@@ -0,0 +1,99 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.processor.data;
|
||||||
|
|
||||||
|
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.payloadURLName;
|
||||||
|
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.phoneNumberName;
|
||||||
|
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.smsProviderAccountIdName;
|
||||||
|
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.smsProviderName;
|
||||||
|
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.smsProviderTokenIdName;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.hooks.domain.HookConfiguration;
|
||||||
|
|
||||||
|
public class SmsProviderData {
|
||||||
|
|
||||||
|
private String url;
|
||||||
|
private String phoneNo;
|
||||||
|
private String smsProvider;
|
||||||
|
private String smsProviderAccountId;
|
||||||
|
private String smsProviderToken;
|
||||||
|
|
||||||
|
private String tenantId;
|
||||||
|
private String mifosToken;
|
||||||
|
private String endpoint;
|
||||||
|
|
||||||
|
public SmsProviderData(final Set<HookConfiguration> config) {
|
||||||
|
|
||||||
|
for (final HookConfiguration conf : config) {
|
||||||
|
final String fieldName = conf.getFieldName();
|
||||||
|
if (fieldName.equals(payloadURLName)) {
|
||||||
|
this.url = conf.getFieldValue();
|
||||||
|
}
|
||||||
|
if (fieldName.equals(smsProviderName)) {
|
||||||
|
this.smsProvider = conf.getFieldValue();
|
||||||
|
}
|
||||||
|
if (fieldName.equals(smsProviderAccountIdName)) {
|
||||||
|
this.smsProviderAccountId = conf.getFieldValue();
|
||||||
|
}
|
||||||
|
if (fieldName.equals(smsProviderTokenIdName)) {
|
||||||
|
this.smsProviderToken = conf.getFieldValue();
|
||||||
|
}
|
||||||
|
if (fieldName.equals(phoneNumberName)) {
|
||||||
|
this.phoneNo = conf.getFieldValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPhoneNumber() {
|
||||||
|
return phoneNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSmsProvider() {
|
||||||
|
return smsProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSmsProviderAccountId() {
|
||||||
|
return smsProviderAccountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSmsProviderTokenId() {
|
||||||
|
return smsProviderToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTenantId() {
|
||||||
|
return tenantId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTenantId(String tenantId) {
|
||||||
|
this.tenantId = tenantId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMifosToken() {
|
||||||
|
return mifosToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMifosToken(String mifosToken) {
|
||||||
|
this.mifosToken = mifosToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEndpoint() {
|
||||||
|
return endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEndpoint(String endpoint) {
|
||||||
|
this.endpoint = endpoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
+102
@@ -0,0 +1,102 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.serialization;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
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.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||||
|
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||||
|
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
|
||||||
|
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||||
|
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class HookCommandFromApiJsonDeserializer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The parameters supported for this command.
|
||||||
|
*/
|
||||||
|
private final Set<String> supportedParameters = new HashSet<>(
|
||||||
|
Arrays.asList("name", "displayName", "isActive", "events", "config"));
|
||||||
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public HookCommandFromApiJsonDeserializer(
|
||||||
|
final FromJsonHelper fromApiJsonHelper) {
|
||||||
|
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validateForCreate(final String json) {
|
||||||
|
if (StringUtils.isBlank(json)) {
|
||||||
|
throw new InvalidJsonException();
|
||||||
|
}
|
||||||
|
|
||||||
|
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
|
||||||
|
}.getType();
|
||||||
|
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
|
||||||
|
this.supportedParameters);
|
||||||
|
|
||||||
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||||
|
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(
|
||||||
|
dataValidationErrors).resource("hook");
|
||||||
|
|
||||||
|
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||||
|
|
||||||
|
final String name = this.fromApiJsonHelper.extractStringNamed("name",
|
||||||
|
element);
|
||||||
|
baseDataValidator.reset().parameter("name").value(name).notBlank()
|
||||||
|
.notExceedingLengthOf(100);
|
||||||
|
|
||||||
|
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validateForUpdate(final String json) {
|
||||||
|
if (StringUtils.isBlank(json)) {
|
||||||
|
throw new InvalidJsonException();
|
||||||
|
}
|
||||||
|
|
||||||
|
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
|
||||||
|
}.getType();
|
||||||
|
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
|
||||||
|
this.supportedParameters);
|
||||||
|
|
||||||
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||||
|
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(
|
||||||
|
dataValidationErrors).resource("hook");
|
||||||
|
|
||||||
|
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||||
|
if (this.fromApiJsonHelper.parameterExists("name", element)) {
|
||||||
|
final String name = this.fromApiJsonHelper.extractStringNamed(
|
||||||
|
"name", element);
|
||||||
|
baseDataValidator.reset().parameter("name").value(name).notBlank()
|
||||||
|
.notExceedingLengthOf(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void throwExceptionIfValidationWarningsExist(
|
||||||
|
final List<ApiParameterError> dataValidationErrors) {
|
||||||
|
if (!dataValidationErrors.isEmpty()) {
|
||||||
|
throw new PlatformApiDataValidationException(
|
||||||
|
"validation.msg.validation.errors.exist",
|
||||||
|
"Validation errors exist.", dataValidationErrors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.service;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.hooks.data.HookData;
|
||||||
|
|
||||||
|
public interface HookReadPlatformService {
|
||||||
|
|
||||||
|
Collection<HookData> retrieveAllHooks();
|
||||||
|
|
||||||
|
HookData retrieveHook(Long hookId);
|
||||||
|
|
||||||
|
HookData retrieveNewHookDetails(String templateName);
|
||||||
|
}
|
||||||
+241
@@ -0,0 +1,241 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
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.mifosplatform.infrastructure.core.domain.JdbcSupport;
|
||||||
|
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
|
||||||
|
import org.mifosplatform.infrastructure.hooks.data.Event;
|
||||||
|
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.exception.HookNotFoundException;
|
||||||
|
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.dao.EmptyResultDataAccessException;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
import org.springframework.jdbc.core.RowMapper;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class HookReadPlatformServiceImpl implements HookReadPlatformService {
|
||||||
|
|
||||||
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public HookReadPlatformServiceImpl(final PlatformSecurityContext context,
|
||||||
|
final RoutingDataSource dataSource) {
|
||||||
|
this.context = context;
|
||||||
|
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<HookData> retrieveAllHooks() {
|
||||||
|
this.context.authenticatedUser();
|
||||||
|
final HookMapper rm = new HookMapper(this.jdbcTemplate);
|
||||||
|
final String sql = "select " + rm.schema() + " order by h.name";
|
||||||
|
|
||||||
|
return this.jdbcTemplate.query(sql, rm, new Object[] {});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HookData retrieveHook(final Long hookId) {
|
||||||
|
try {
|
||||||
|
this.context.authenticatedUser();
|
||||||
|
final HookMapper rm = new HookMapper(this.jdbcTemplate);
|
||||||
|
final String sql = "select " + rm.schema() + " where h.id = ?";
|
||||||
|
|
||||||
|
return this.jdbcTemplate.queryForObject(sql, rm,
|
||||||
|
new Object[] { hookId });
|
||||||
|
} catch (final EmptyResultDataAccessException e) {
|
||||||
|
throw new HookNotFoundException(hookId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HookData retrieveNewHookDetails(final String templateName) {
|
||||||
|
|
||||||
|
this.context.authenticatedUser();
|
||||||
|
final TemplateMapper rm = new TemplateMapper(this.jdbcTemplate);
|
||||||
|
final String sql;
|
||||||
|
List<HookTemplateData> templateData;
|
||||||
|
|
||||||
|
if (templateName == null) {
|
||||||
|
sql = "select " + rm.schema() + " order by s.name";
|
||||||
|
templateData = this.jdbcTemplate.query(sql, rm, new Object[] {});
|
||||||
|
} else {
|
||||||
|
sql = "select " + rm.schema() + " where s.name = ? order by s.name";
|
||||||
|
templateData = this.jdbcTemplate.query(sql, rm,
|
||||||
|
new Object[] { templateName });
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<Grouping> events = getTemplateForEvents();
|
||||||
|
|
||||||
|
return HookData.template(templateData, events);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Grouping> getTemplateForEvents() {
|
||||||
|
final String sql = "select p.grouping, p.entity_name, p.action_name from m_permission p "
|
||||||
|
+ " where p.action_name NOT LIKE '%CHECKER%' AND p.action_name NOT LIKE '%READ%' "
|
||||||
|
+ " order by p.grouping, p.entity_name ";
|
||||||
|
final EventResultSetExtractor extractor = new EventResultSetExtractor();
|
||||||
|
return this.jdbcTemplate.query(sql, extractor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class HookMapper implements RowMapper<HookData> {
|
||||||
|
|
||||||
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
|
||||||
|
public HookMapper(final JdbcTemplate jdbcTemplate) {
|
||||||
|
this.jdbcTemplate = jdbcTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String schema() {
|
||||||
|
return " h.id, s.name as name, h.name as display_name, h.is_active, h.created_date, h.lastmodified_date "
|
||||||
|
+ " from m_hook h inner join m_hook_templates s on h.template_id = s.id ";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HookData mapRow(final ResultSet rs,
|
||||||
|
@SuppressWarnings("unused") final int rowNum)
|
||||||
|
throws SQLException {
|
||||||
|
|
||||||
|
final Long id = rs.getLong("id");
|
||||||
|
final String name = rs.getString("name");
|
||||||
|
final String displayname = rs.getString("display_name");
|
||||||
|
final boolean isActive = rs.getBoolean("is_active");
|
||||||
|
final LocalDate createdAt = JdbcSupport.getLocalDate(rs,
|
||||||
|
"created_date");
|
||||||
|
final LocalDate updatedAt = JdbcSupport.getLocalDate(rs,
|
||||||
|
"lastmodified_date");
|
||||||
|
final List<Event> registeredEvents = retrieveEvents(id);
|
||||||
|
final List<Field> config = retrieveConfig(id);
|
||||||
|
|
||||||
|
return HookData.instance(id, name, displayname, isActive,
|
||||||
|
createdAt, updatedAt, registeredEvents, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Event> retrieveEvents(final Long hookId) {
|
||||||
|
|
||||||
|
final HookEventMapper rm = new HookEventMapper();
|
||||||
|
final String sql = "select " + rm.schema() + " where h.id= ?";
|
||||||
|
|
||||||
|
return this.jdbcTemplate.query(sql, rm, new Object[] { hookId });
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Field> retrieveConfig(final Long hookId) {
|
||||||
|
|
||||||
|
final HookConfigMapper rm = new HookConfigMapper();
|
||||||
|
final String sql = "select " + rm.schema()
|
||||||
|
+ " where h.id= ? order by hc.field_name";
|
||||||
|
|
||||||
|
final List<Field> fields = this.jdbcTemplate.query(sql, rm,
|
||||||
|
new Object[] { hookId });
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class HookEventMapper implements RowMapper<Event> {
|
||||||
|
|
||||||
|
public String schema() {
|
||||||
|
return " re.action_name, re.entity_name from m_hook h inner join m_hook_registered_events re on h.id = re.hook_id ";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Event mapRow(final ResultSet rs,
|
||||||
|
@SuppressWarnings("unused") final int rowNum)
|
||||||
|
throws SQLException {
|
||||||
|
final String actionName = rs.getString("action_name");
|
||||||
|
final String entityName = rs.getString("entity_name");
|
||||||
|
return Event.instance(actionName, entityName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class HookConfigMapper implements RowMapper<Field> {
|
||||||
|
|
||||||
|
public String schema() {
|
||||||
|
return " hc.field_name, hc.field_value from m_hook h inner join m_hook_configuration hc on h.id = hc.hook_id ";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Field mapRow(final ResultSet rs,
|
||||||
|
@SuppressWarnings("unused") final int rowNum)
|
||||||
|
throws SQLException {
|
||||||
|
final String fieldName = rs.getString("field_name");
|
||||||
|
final String fieldValue = rs.getString("field_value");
|
||||||
|
return Field.fromConfig(fieldName, fieldValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class TemplateMapper implements
|
||||||
|
RowMapper<HookTemplateData> {
|
||||||
|
|
||||||
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
|
||||||
|
public TemplateMapper(final JdbcTemplate jdbcTemplate) {
|
||||||
|
this.jdbcTemplate = jdbcTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String schema() {
|
||||||
|
return " s.id, s.name from m_hook_templates s ";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HookTemplateData mapRow(final ResultSet rs,
|
||||||
|
@SuppressWarnings("unused") final int rowNum)
|
||||||
|
throws SQLException {
|
||||||
|
|
||||||
|
final Long id = rs.getLong("id");
|
||||||
|
final String name = rs.getString("name");
|
||||||
|
final List<Field> schema = retrieveSchema(id);
|
||||||
|
|
||||||
|
return HookTemplateData.instance(id, name, schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Field> retrieveSchema(final Long templateId) {
|
||||||
|
|
||||||
|
final TemplateSchemaMapper rm = new TemplateSchemaMapper();
|
||||||
|
final String sql = "select " + rm.schema()
|
||||||
|
+ " where s.id= ? order by hs.field_name ";
|
||||||
|
|
||||||
|
final List<Field> fields = this.jdbcTemplate.query(sql, rm,
|
||||||
|
new Object[] { templateId });
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class TemplateSchemaMapper implements RowMapper<Field> {
|
||||||
|
|
||||||
|
public String schema() {
|
||||||
|
return " hs.field_type, hs.field_name, hs.placeholder, hs.optional from m_hook_templates s "
|
||||||
|
+ " inner join m_hook_schema hs on s.id = hs.hook_template_id ";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Field mapRow(final ResultSet rs,
|
||||||
|
@SuppressWarnings("unused") final int rowNum)
|
||||||
|
throws SQLException {
|
||||||
|
final String fieldName = rs.getString("field_name");
|
||||||
|
final String fieldType = rs.getString("field_type");
|
||||||
|
final Boolean optional = rs.getBoolean("optional");
|
||||||
|
final String placeholder = rs.getString("placeholder");
|
||||||
|
return Field
|
||||||
|
.fromSchema(fieldType, fieldName, optional, placeholder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.service;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
|
||||||
|
public interface HookWritePlatformService {
|
||||||
|
|
||||||
|
CommandProcessingResult createHook(JsonCommand command);
|
||||||
|
|
||||||
|
CommandProcessingResult updateHook(Long hookId, JsonCommand command);
|
||||||
|
|
||||||
|
CommandProcessingResult deleteHook(Long hookId);
|
||||||
|
|
||||||
|
}
|
||||||
+292
@@ -0,0 +1,292 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.infrastructure.hooks.service;
|
||||||
|
|
||||||
|
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.actionNameParamName;
|
||||||
|
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.configParamName;
|
||||||
|
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.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.data.ApiParameterError;
|
||||||
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||||
|
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||||
|
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||||
|
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||||
|
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||||
|
import org.mifosplatform.infrastructure.hooks.domain.Hook;
|
||||||
|
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.HookTemplateNotFoundException;
|
||||||
|
import org.mifosplatform.infrastructure.hooks.serialization.HookCommandFromApiJsonDeserializer;
|
||||||
|
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.dao.DataIntegrityViolationException;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class HookWritePlatformServiceJpaRepositoryImpl implements
|
||||||
|
HookWritePlatformService {
|
||||||
|
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
private final HookRepository hookRepository;
|
||||||
|
private final HookTemplateRepository hookTemplateRepository;
|
||||||
|
private final HookCommandFromApiJsonDeserializer fromApiJsonDeserializer;
|
||||||
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public HookWritePlatformServiceJpaRepositoryImpl(
|
||||||
|
final PlatformSecurityContext context,
|
||||||
|
final HookRepository hookRepository,
|
||||||
|
final HookTemplateRepository hookTemplateRepository,
|
||||||
|
final HookCommandFromApiJsonDeserializer fromApiJsonDeserializer,
|
||||||
|
final FromJsonHelper fromApiJsonHelper) {
|
||||||
|
this.context = context;
|
||||||
|
this.hookRepository = hookRepository;
|
||||||
|
this.hookTemplateRepository = hookTemplateRepository;
|
||||||
|
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
|
||||||
|
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult createHook(final JsonCommand command) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.context.authenticatedUser();
|
||||||
|
|
||||||
|
this.fromApiJsonDeserializer.validateForCreate(command.json());
|
||||||
|
|
||||||
|
final HookTemplate template = retrieveHookTemplateBy(command
|
||||||
|
.stringValueOfParameterNamed(nameParamName));
|
||||||
|
final String configJson = command.jsonFragment(configParamName);
|
||||||
|
final Set<HookConfiguration> config = assembleConfig(
|
||||||
|
command.mapValueOfParameterNamed(configJson), template);
|
||||||
|
final JsonArray events = command
|
||||||
|
.arrayOfParameterNamed(eventsParamName);
|
||||||
|
final Set<HookResource> allEvents = assembleSetOfEvents(events);
|
||||||
|
final Hook hook = Hook.fromJson(command, template, config,
|
||||||
|
allEvents);
|
||||||
|
|
||||||
|
validateHookRules(template, config);
|
||||||
|
|
||||||
|
this.hookRepository.save(hook);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder()
|
||||||
|
.withCommandId(command.commandId())
|
||||||
|
.withEntityId(hook.getId()).build();
|
||||||
|
} catch (final DataIntegrityViolationException dve) {
|
||||||
|
handleHookDataIntegrityIssues(command, dve);
|
||||||
|
return CommandProcessingResult.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult updateHook(final Long hookId,
|
||||||
|
final JsonCommand command) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.context.authenticatedUser();
|
||||||
|
|
||||||
|
this.fromApiJsonDeserializer.validateForUpdate(command.json());
|
||||||
|
|
||||||
|
final Hook hook = retrieveHookBy(hookId);
|
||||||
|
final HookTemplate template = hook.getHookTemplate();
|
||||||
|
final Map<String, Object> changes = hook.update(command);
|
||||||
|
|
||||||
|
if (!changes.isEmpty()) {
|
||||||
|
|
||||||
|
if (changes.containsKey(eventsParamName)) {
|
||||||
|
final Set<HookResource> events = assembleSetOfEvents(command
|
||||||
|
.arrayOfParameterNamed(eventsParamName));
|
||||||
|
final boolean updated = hook.updateEvents(events);
|
||||||
|
if (!updated) {
|
||||||
|
changes.remove(eventsParamName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changes.containsKey(configParamName)) {
|
||||||
|
final String configJson = command
|
||||||
|
.jsonFragment(configParamName);
|
||||||
|
final Set<HookConfiguration> config = assembleConfig(
|
||||||
|
command.mapValueOfParameterNamed(configJson),
|
||||||
|
template);
|
||||||
|
final boolean updated = hook.updateConfig(config);
|
||||||
|
if (!updated) {
|
||||||
|
changes.remove(configParamName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hookRepository.saveAndFlush(hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder() //
|
||||||
|
.withCommandId(command.commandId()) //
|
||||||
|
.withEntityId(hookId) //
|
||||||
|
.with(changes) //
|
||||||
|
.build();
|
||||||
|
} catch (final DataIntegrityViolationException dve) {
|
||||||
|
handleHookDataIntegrityIssues(command, dve);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult deleteHook(final Long hookId) {
|
||||||
|
|
||||||
|
this.context.authenticatedUser();
|
||||||
|
|
||||||
|
final Hook hook = retrieveHookBy(hookId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.hookRepository.delete(hook);
|
||||||
|
this.hookRepository.flush();
|
||||||
|
} catch (final DataIntegrityViolationException e) {
|
||||||
|
throw new PlatformDataIntegrityException(
|
||||||
|
"error.msg.unknown.data.integrity.issue",
|
||||||
|
"Unknown data integrity issue with resource: "
|
||||||
|
+ e.getMostSpecificCause());
|
||||||
|
}
|
||||||
|
return new CommandProcessingResultBuilder().withEntityId(hookId)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Hook retrieveHookBy(final Long hookId) {
|
||||||
|
final Hook hook = this.hookRepository.findOne(hookId);
|
||||||
|
if (hook == null) {
|
||||||
|
throw new HookNotFoundException(hookId.toString());
|
||||||
|
}
|
||||||
|
return hook;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HookTemplate retrieveHookTemplateBy(final String templateName) {
|
||||||
|
final HookTemplate template = this.hookTemplateRepository
|
||||||
|
.findOne(templateName);
|
||||||
|
if (template == null) {
|
||||||
|
throw new HookTemplateNotFoundException(templateName);
|
||||||
|
}
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<HookConfiguration> assembleConfig(
|
||||||
|
final Map<String, String> hookConfig, final HookTemplate template) {
|
||||||
|
|
||||||
|
final Set<HookConfiguration> configuration = new HashSet<>();
|
||||||
|
final Set<Schema> fields = template.getSchema();
|
||||||
|
|
||||||
|
for (final Entry<String, String> configEntry : hookConfig.entrySet()) {
|
||||||
|
for (final Schema field : fields) {
|
||||||
|
if (field.getFieldName().equalsIgnoreCase(configEntry.getKey())) {
|
||||||
|
final HookConfiguration config = HookConfiguration
|
||||||
|
.createNewWithoutHook(field.getFieldType(),
|
||||||
|
configEntry.getKey(),
|
||||||
|
configEntry.getValue());
|
||||||
|
configuration.add(config);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<HookResource> assembleSetOfEvents(final JsonArray eventsArray) {
|
||||||
|
|
||||||
|
final Set<HookResource> allEvents = new HashSet<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < eventsArray.size(); i++) {
|
||||||
|
|
||||||
|
final JsonObject eventElement = eventsArray.get(i)
|
||||||
|
.getAsJsonObject();
|
||||||
|
|
||||||
|
final String entityName = this.fromApiJsonHelper
|
||||||
|
.extractStringNamed(entityNameParamName, eventElement);
|
||||||
|
final String actionName = this.fromApiJsonHelper
|
||||||
|
.extractStringNamed(actionNameParamName, eventElement);
|
||||||
|
final HookResource event = HookResource.createNewWithoutHook(
|
||||||
|
entityName, actionName);
|
||||||
|
allEvents.add(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
return allEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateHookRules(final HookTemplate template,
|
||||||
|
final Set<HookConfiguration> config) {
|
||||||
|
|
||||||
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||||
|
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(
|
||||||
|
dataValidationErrors).resource("hook");
|
||||||
|
|
||||||
|
if (!template.getName().equalsIgnoreCase(webTemplateName)
|
||||||
|
&& this.hookRepository.findOneByTemplateId(template.getId()) != null) {
|
||||||
|
final String errorMessage = "multiple.non.web.template.hooks.not.supported";
|
||||||
|
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
|
||||||
|
errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Set<Schema> fields = template.getSchema();
|
||||||
|
for (final Schema field : fields) {
|
||||||
|
if (!field.isOptional()) {
|
||||||
|
boolean found = false;
|
||||||
|
for (final HookConfiguration conf : config) {
|
||||||
|
if (field.getFieldName().equals(conf.getFieldName())) {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
final String errorMessage = "required.config.field."
|
||||||
|
+ field.getFieldName() + ".not.provided";
|
||||||
|
baseDataValidator.reset()
|
||||||
|
.failWithCodeNoParameterAddedToErrorCode(
|
||||||
|
errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dataValidationErrors.isEmpty()) {
|
||||||
|
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleHookDataIntegrityIssues(final JsonCommand command,
|
||||||
|
final DataIntegrityViolationException dve) {
|
||||||
|
final Throwable realCause = dve.getMostSpecificCause();
|
||||||
|
if (realCause.getMessage().contains("hook_name")) {
|
||||||
|
final String name = command.stringValueOfParameterNamed("name");
|
||||||
|
throw new PlatformDataIntegrityException(
|
||||||
|
"error.msg.hook.duplicate.name", "A hook with name '"
|
||||||
|
+ name + "' already exists", "name", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new PlatformDataIntegrityException(
|
||||||
|
"error.msg.unknown.data.integrity.issue",
|
||||||
|
"Unknown data integrity issue with resource: "
|
||||||
|
+ realCause.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
-2
@@ -53,7 +53,7 @@ public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFil
|
|||||||
|
|
||||||
private static boolean firstRequestProcessed = false;
|
private static boolean firstRequestProcessed = false;
|
||||||
private final static Logger logger = LoggerFactory.getLogger(TenantAwareBasicAuthenticationFilter.class);
|
private final static Logger logger = LoggerFactory.getLogger(TenantAwareBasicAuthenticationFilter.class);
|
||||||
|
|
||||||
private final BasicAuthTenantDetailsService basicAuthTenantDetailsService;
|
private final BasicAuthTenantDetailsService basicAuthTenantDetailsService;
|
||||||
private final ToApiJsonSerializer<PlatformRequestLog> toApiJsonSerializer;
|
private final ToApiJsonSerializer<PlatformRequestLog> toApiJsonSerializer;
|
||||||
private final ConfigurationDomainService configurationDomainService;
|
private final ConfigurationDomainService configurationDomainService;
|
||||||
@@ -103,8 +103,16 @@ public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFil
|
|||||||
final MifosPlatformTenant tenant = this.basicAuthTenantDetailsService.loadTenantById(tenantIdentifier);
|
final MifosPlatformTenant tenant = this.basicAuthTenantDetailsService.loadTenantById(tenantIdentifier);
|
||||||
|
|
||||||
ThreadLocalContextUtil.setTenant(tenant);
|
ThreadLocalContextUtil.setTenant(tenant);
|
||||||
|
String authToken = request.getHeader("Authorization");
|
||||||
|
|
||||||
|
if (authToken != null && authToken.startsWith("Basic ")) {
|
||||||
|
ThreadLocalContextUtil.setAuthToken(authToken.replaceFirst("Basic ", ""));
|
||||||
|
}
|
||||||
|
|
||||||
if (!firstRequestProcessed) {
|
if (!firstRequestProcessed) {
|
||||||
|
final String baseUrl = request.getRequestURL().toString().replace(request.getPathInfo(), "/");
|
||||||
|
System.setProperty("baseUrl", baseUrl);
|
||||||
|
|
||||||
final boolean ehcacheEnabled = this.configurationDomainService.isEhcacheEnabled();
|
final boolean ehcacheEnabled = this.configurationDomainService.isEhcacheEnabled();
|
||||||
if (ehcacheEnabled) {
|
if (ehcacheEnabled) {
|
||||||
this.cacheWritePlatformService.switchToCache(CacheType.SINGLE_NODE);
|
this.cacheWritePlatformService.switchToCache(CacheType.SINGLE_NODE);
|
||||||
@@ -114,7 +122,7 @@ public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFil
|
|||||||
TenantAwareBasicAuthenticationFilter.firstRequestProcessed = true;
|
TenantAwareBasicAuthenticationFilter.firstRequestProcessed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.doFilter(req, res, chain);
|
super.doFilter(req, res, chain);
|
||||||
} catch (final InvalidTenantIdentiferException e) {
|
} catch (final InvalidTenantIdentiferException e) {
|
||||||
// deal with exception at low level
|
// deal with exception at low level
|
||||||
|
|||||||
@@ -72,4 +72,10 @@
|
|||||||
|
|
||||||
<import resource="cache.xml" />
|
<import resource="cache.xml" />
|
||||||
|
|
||||||
|
<bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">
|
||||||
|
<property name="taskExecutor">
|
||||||
|
<bean class="org.springframework.core.task.SimpleAsyncTaskExecutor"/>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
|||||||
Reference in New Issue
Block a user