@universis/webhooks 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitlab-ci.yml +40 -0
- package/LICENSE +163 -0
- package/README.md +113 -0
- package/dist/SchemaLoader.d.ts +4 -0
- package/dist/SchemaLoader.js +46 -0
- package/dist/SchemaLoader.js.map +1 -0
- package/dist/WebHookService.d.ts +11 -0
- package/dist/WebHookService.js +272 -0
- package/dist/WebHookService.js.map +1 -0
- package/dist/config/models/WebHook.json +182 -0
- package/dist/config/models/WebHookAction.json +85 -0
- package/dist/config/models/WebHookConfiguration.json +66 -0
- package/dist/config/scope.access.js +30 -0
- package/dist/config/scope.access.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/listeners/OnWebHook.js +33 -0
- package/dist/listeners/OnWebHook.js.map +1 -0
- package/dist/listeners/OnWebHookAction.js +53 -0
- package/dist/listeners/OnWebHookAction.js.map +1 -0
- package/dist/listeners/package.json +3 -0
- package/jest.config.js +202 -0
- package/jsconfig.json +8 -0
- package/package.json +65 -0
- package/src/SchemaLoader.d.ts +4 -0
- package/src/SchemaLoader.js +46 -0
- package/src/WebHookService.d.ts +11 -0
- package/src/WebHookService.js +275 -0
- package/src/config/models/WebHook.json +182 -0
- package/src/config/models/WebHookAction.json +85 -0
- package/src/config/models/WebHookConfiguration.json +66 -0
- package/src/config/scope.access.js +34 -0
- package/src/index.d.ts +2 -0
- package/src/index.js +2 -0
- package/src/listeners/OnWebHook.js +37 -0
- package/src/listeners/OnWebHookAction.js +56 -0
- package/src/listeners/package.json +3 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://themost-framework.github.io/themost/models/2018/2/schema.json",
|
|
3
|
+
"@id": "https://universis.io/schemas/WebHook",
|
|
4
|
+
"name": "WebHook",
|
|
5
|
+
"title": "WebHooks",
|
|
6
|
+
"hidden": false,
|
|
7
|
+
"sealed": false,
|
|
8
|
+
"abstract": false,
|
|
9
|
+
"version": "1.1.0",
|
|
10
|
+
"fields": [
|
|
11
|
+
{
|
|
12
|
+
"@id": "https://universis.io#id",
|
|
13
|
+
"name": "id",
|
|
14
|
+
"title": "ID",
|
|
15
|
+
"description": "The identifier of the item.",
|
|
16
|
+
"type": "Guid",
|
|
17
|
+
"primary": true,
|
|
18
|
+
"readonly": true,
|
|
19
|
+
"value": {
|
|
20
|
+
"$uuid": 1
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"@id": "http://schema.org/additionalType",
|
|
25
|
+
"name": "additionalType",
|
|
26
|
+
"title": "additionalType",
|
|
27
|
+
"description": "An additional type for the item, typically used for adding more specific types from external vocabularies in microdata syntax. This is a relationship between something and a class that the thing is in. In RDFa syntax, it is better to use the native RDFa syntax - the 'typeof' attribute - for multiple types. Schema.org tools may have only weaker understanding of extra types, in particular those defined externally.",
|
|
28
|
+
"type": "Text",
|
|
29
|
+
"value": "javascript:return this.model.name;",
|
|
30
|
+
"readonly": true
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"@id": "http://schema.org/name",
|
|
34
|
+
"name": "name",
|
|
35
|
+
"title": "name",
|
|
36
|
+
"description": "The name of the item.",
|
|
37
|
+
"type": "Text"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"@id": "https://universis.io#dateCreated",
|
|
41
|
+
"name": "dateCreated",
|
|
42
|
+
"title": "dateCreated",
|
|
43
|
+
"description": "The date on which this item was created.",
|
|
44
|
+
"type": "DateTime",
|
|
45
|
+
"readonly": true,
|
|
46
|
+
"value": {
|
|
47
|
+
"$now": 1
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"@id": "https://universis.io#dateModified",
|
|
52
|
+
"name": "dateModified",
|
|
53
|
+
"title": "dateModified",
|
|
54
|
+
"description": "The date on which this item was most recently modified.",
|
|
55
|
+
"type": "DateTime",
|
|
56
|
+
"readonly": true,
|
|
57
|
+
"value": {
|
|
58
|
+
"$now": 1
|
|
59
|
+
},
|
|
60
|
+
"calculation": {
|
|
61
|
+
"$now": 1
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"@id": "https://universis.io#owner",
|
|
66
|
+
"name": "owner",
|
|
67
|
+
"title": "owner",
|
|
68
|
+
"description": "The owner of the item.",
|
|
69
|
+
"type": "User",
|
|
70
|
+
"editable": false,
|
|
71
|
+
"nullable": false,
|
|
72
|
+
"value": {
|
|
73
|
+
"$user": 1
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"@id": "https://universis.io#emit",
|
|
78
|
+
"name": "emit",
|
|
79
|
+
"title": "Emit",
|
|
80
|
+
"description": "Specifies whether the webhook should be triggered automatically or not.",
|
|
81
|
+
"type": "Boolean",
|
|
82
|
+
"value": {
|
|
83
|
+
"$value": true
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"@id": "https://universis.io#createdBy",
|
|
88
|
+
"name": "createdBy",
|
|
89
|
+
"title": "createdBy",
|
|
90
|
+
"description": "Created by user.",
|
|
91
|
+
"type": "User",
|
|
92
|
+
"readonly": true,
|
|
93
|
+
"value": {
|
|
94
|
+
"$user": 1
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"@id": "https://universis.io#modifiedBy",
|
|
99
|
+
"name": "modifiedBy",
|
|
100
|
+
"title": "modifiedBy",
|
|
101
|
+
"description": "Last modified by user.",
|
|
102
|
+
"type": "User",
|
|
103
|
+
"readonly": true,
|
|
104
|
+
"value": {
|
|
105
|
+
"$user": 1
|
|
106
|
+
},
|
|
107
|
+
"calculation": {
|
|
108
|
+
"$user": 1
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"@id": "https://universis.io#webHookAction",
|
|
113
|
+
"name": "action",
|
|
114
|
+
"title": "action",
|
|
115
|
+
"description": "Reference to WebHookAction entity that defines the scope of the webhook.",
|
|
116
|
+
"type": "WebHookAction",
|
|
117
|
+
"mapping": {
|
|
118
|
+
"parentModel": "WebHookAction",
|
|
119
|
+
"parentField": "name"
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"@id": "https://universis.io#active",
|
|
124
|
+
"name": "active",
|
|
125
|
+
"title": "Active",
|
|
126
|
+
"description": "Specifies whether the webhook is active or not.",
|
|
127
|
+
"type": "Boolean",
|
|
128
|
+
"nullable": false,
|
|
129
|
+
"value": {
|
|
130
|
+
"$value": true
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"@id": "https://universis.io#webHookConfig",
|
|
135
|
+
"name": "config",
|
|
136
|
+
"title": "WebHook endpoint configuration",
|
|
137
|
+
"description": "Configuration settings for the webhook endpoint e.g. authentication, custom headers etc.",
|
|
138
|
+
"type": "Json",
|
|
139
|
+
"additionalType": "WebHookConfiguration"
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"@id": "https://universis.io#lastResponse",
|
|
143
|
+
"name": "lastResponse",
|
|
144
|
+
"title": "Last Response",
|
|
145
|
+
"description": "Details of the last response received from the webhook endpoint.",
|
|
146
|
+
"type": "Json"
|
|
147
|
+
}
|
|
148
|
+
],
|
|
149
|
+
"privileges": [
|
|
150
|
+
{
|
|
151
|
+
"mask": 15,
|
|
152
|
+
"type": "global"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"mask": 15,
|
|
156
|
+
"type": "global",
|
|
157
|
+
"account": "Administrators"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"mask": 1,
|
|
161
|
+
"type": "self",
|
|
162
|
+
"filter": "owner eq me()",
|
|
163
|
+
"scope": [
|
|
164
|
+
"webhooks:read"
|
|
165
|
+
]
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"mask": 14,
|
|
169
|
+
"type": "self",
|
|
170
|
+
"filter": "owner eq me()",
|
|
171
|
+
"scope": [
|
|
172
|
+
"webhooks:write"
|
|
173
|
+
]
|
|
174
|
+
}
|
|
175
|
+
],
|
|
176
|
+
"constraints": [
|
|
177
|
+
],
|
|
178
|
+
"seed": [
|
|
179
|
+
],
|
|
180
|
+
"eventListeners": [
|
|
181
|
+
]
|
|
182
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://themost-framework.github.io/themost/models/2018/2/schema.json",
|
|
3
|
+
"@id": "https://universis.io/schemas/WebHookAction",
|
|
4
|
+
"name": "WebHookAction",
|
|
5
|
+
"title": "WebHookActions",
|
|
6
|
+
"hidden": false,
|
|
7
|
+
"sealed": false,
|
|
8
|
+
"abstract": false,
|
|
9
|
+
"implements": "Thing",
|
|
10
|
+
"version": "1.1.0",
|
|
11
|
+
"fields": [
|
|
12
|
+
{
|
|
13
|
+
"@id": "http://schema.org/name",
|
|
14
|
+
"name": "name",
|
|
15
|
+
"title": "name",
|
|
16
|
+
"description": "The name of the item.",
|
|
17
|
+
"type": "Text",
|
|
18
|
+
"nullable": false
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"@id": "http://schema.org/about",
|
|
22
|
+
"name": "about",
|
|
23
|
+
"type": "Text",
|
|
24
|
+
"title": "About",
|
|
25
|
+
"description": "The name of the entity type to which the webhook applies",
|
|
26
|
+
"nullable": false
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"@id": "https://universis.io#state",
|
|
30
|
+
"name": "state",
|
|
31
|
+
"type": "Integer",
|
|
32
|
+
"title": "Event Types",
|
|
33
|
+
"description": "The type of event that triggers the webhook (e.g., insert, update, delete)",
|
|
34
|
+
"nullable": false
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"@id": "https://universis.io#filter",
|
|
38
|
+
"name": "filter",
|
|
39
|
+
"type": "Text",
|
|
40
|
+
"title": "Filter",
|
|
41
|
+
"description": "Specifies when the webhook should be executed according to the current state of an object. The expression should follow OData $filter query option e.g. studentStatus ne 'active'",
|
|
42
|
+
"nullable": true
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"@id": "https://universis.io#when",
|
|
46
|
+
"name": "when",
|
|
47
|
+
"type": "Text",
|
|
48
|
+
"title": "When",
|
|
49
|
+
"description": "An optional filter expression to further refine when the webhook is triggered according to the previous state of an object. The expression should follow OData $filter query option e.g. studentStatus eq 'active'",
|
|
50
|
+
"nullable": true
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
"privileges": [
|
|
54
|
+
{
|
|
55
|
+
"mask": 15,
|
|
56
|
+
"type": "global"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"mask": 1,
|
|
60
|
+
"type": "global",
|
|
61
|
+
"account": "*"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"mask": 15,
|
|
65
|
+
"type": "global",
|
|
66
|
+
"account": "Administrators"
|
|
67
|
+
}
|
|
68
|
+
],
|
|
69
|
+
"constraints": [
|
|
70
|
+
{
|
|
71
|
+
"type": "unique",
|
|
72
|
+
"@help": "Each item name must be unique because it is used as a reference e.g. create_user or delete_user etc.",
|
|
73
|
+
"fields": [
|
|
74
|
+
"name"
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
"seed": [
|
|
79
|
+
],
|
|
80
|
+
"eventListeners": [
|
|
81
|
+
{
|
|
82
|
+
"type": "./listeners/OnWebHookAction"
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://themost-framework.github.io/themost/models/2018/2/schema.json",
|
|
3
|
+
"@id": "https://universis.io/schemas/WebHookConfiguration",
|
|
4
|
+
"name": "WebHookConfiguration",
|
|
5
|
+
"title": "WebHookConfigurations",
|
|
6
|
+
"hidden": true,
|
|
7
|
+
"sealed": true,
|
|
8
|
+
"abstract": true,
|
|
9
|
+
"version": "1.1.0",
|
|
10
|
+
"fields": [
|
|
11
|
+
{
|
|
12
|
+
"@id": "https://universis.io#url",
|
|
13
|
+
"name": "url",
|
|
14
|
+
"title": "URL",
|
|
15
|
+
"description": "The URL to which the webhook payloads will be delivered",
|
|
16
|
+
"type": "URL",
|
|
17
|
+
"nullable": false
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"@id": "https://universis.io#contentType",
|
|
21
|
+
"name": "contentType",
|
|
22
|
+
"title": "Content Type",
|
|
23
|
+
"description": "The content type of the payload sent to the webhook URL (e.g., application/json)",
|
|
24
|
+
"type": "Text",
|
|
25
|
+
"nullable": false,
|
|
26
|
+
"value": {
|
|
27
|
+
"$value": "application/json"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"@id": "https://universis.io#insecure",
|
|
32
|
+
"name": "insecure",
|
|
33
|
+
"title": "Insecure",
|
|
34
|
+
"description": "Indicates whether to skip SSL certificate verification when sending requests to the webhook URL",
|
|
35
|
+
"type": "Boolean",
|
|
36
|
+
"nullable": false,
|
|
37
|
+
"value": {
|
|
38
|
+
"$value": false
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"@id": "https://universis.io#secret",
|
|
43
|
+
"name": "secret",
|
|
44
|
+
"title": "Secret",
|
|
45
|
+
"description": "An optional secret key used to sign the payloads sent to the webhook URL for verification purposes. The signature is included in the 'X-Signature' header of the request.",
|
|
46
|
+
"type": "Text"
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"privileges": [
|
|
50
|
+
{
|
|
51
|
+
"mask": 15,
|
|
52
|
+
"type": "global"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"mask": 15,
|
|
56
|
+
"type": "global",
|
|
57
|
+
"account": "Administrators"
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
"constraints": [
|
|
61
|
+
],
|
|
62
|
+
"seed": [
|
|
63
|
+
],
|
|
64
|
+
"eventListeners": [
|
|
65
|
+
]
|
|
66
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const WebHookScopeAccessElements = [
|
|
2
|
+
{
|
|
3
|
+
'scope': [
|
|
4
|
+
'webhooks:read',
|
|
5
|
+
],
|
|
6
|
+
'resource': '/api/WebHookActions/?',
|
|
7
|
+
'access': [
|
|
8
|
+
'read'
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
'scope': [
|
|
13
|
+
'webhooks:read',
|
|
14
|
+
],
|
|
15
|
+
'resource': '/api/WebHooks/?',
|
|
16
|
+
'access': [
|
|
17
|
+
'read'
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
'scope': [
|
|
22
|
+
'webhooks:write',
|
|
23
|
+
],
|
|
24
|
+
'resource': '/api/WebHooks/?',
|
|
25
|
+
'access': [
|
|
26
|
+
'read',
|
|
27
|
+
'write'
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
export {
|
|
33
|
+
WebHookScopeAccessElements
|
|
34
|
+
}
|
package/src/index.d.ts
ADDED
package/src/index.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {WebHookService} from '../WebHookService';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A generic web hook listener for insert or update operations to any data model
|
|
5
|
+
* @param {import('@themost/data').DataEventArgs} event
|
|
6
|
+
* @param {Function} callback
|
|
7
|
+
*/
|
|
8
|
+
function afterSave(event, callback) {
|
|
9
|
+
return afterEvent(event, callback);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* @param {import('@themost/data').DataEventArgs} event
|
|
13
|
+
* @param {Function} callback
|
|
14
|
+
*/
|
|
15
|
+
function afterEvent(event, callback) {
|
|
16
|
+
const {context} = event.model;
|
|
17
|
+
const service = context.application.getService(WebHookService);
|
|
18
|
+
if (service) {
|
|
19
|
+
service.emit(event);
|
|
20
|
+
}
|
|
21
|
+
return callback();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A generic web hook for remove operations to any data model
|
|
26
|
+
* @param {import('@themost/data').DataEventArgs} event
|
|
27
|
+
* @param {Function} callback
|
|
28
|
+
*/
|
|
29
|
+
function afterRemove(event, callback) {
|
|
30
|
+
|
|
31
|
+
return afterEvent(event, callback);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export {
|
|
35
|
+
afterSave,
|
|
36
|
+
afterRemove
|
|
37
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {DataObjectState, SchemaLoaderStrategy} from '@themost/data';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
/**
|
|
4
|
+
* After save listener for OneWebHookAction
|
|
5
|
+
* @param {import('@themost/data').DataEventArgs} event
|
|
6
|
+
* @param {Function} callback
|
|
7
|
+
*/
|
|
8
|
+
function afterSave(event, callback) {
|
|
9
|
+
if (event.state === DataObjectState.Insert || event.state === DataObjectState.Update) {
|
|
10
|
+
// change the underlying model definition
|
|
11
|
+
const { context } = event.model;
|
|
12
|
+
const schemaLoader = context.getConfiguration().getStrategy(SchemaLoaderStrategy);
|
|
13
|
+
const { about } = event.target;
|
|
14
|
+
if (about) {
|
|
15
|
+
/**
|
|
16
|
+
* @type {import('@themost/common').DataModelProperties}
|
|
17
|
+
*/
|
|
18
|
+
const schema = schemaLoader.getModelDefinition(about);
|
|
19
|
+
if (schema) {
|
|
20
|
+
// try to find of previous state listener should be included in the list of event listeners
|
|
21
|
+
schema.eventListeners = schema.eventListeners || [];
|
|
22
|
+
const index = schema.eventListeners.findIndex((item) => {
|
|
23
|
+
return item.type === '@themost/data#previous-state-listener';
|
|
24
|
+
});
|
|
25
|
+
if (index < 0) {
|
|
26
|
+
// insert previous state listener at the beginning of the list
|
|
27
|
+
schema.eventListeners.unshift({
|
|
28
|
+
type: '@themost/data/previous-state-listener'
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const type = path.resolve(__dirname, './OnWebHook');
|
|
32
|
+
const id = '@universis/webhooks/listeners/OnWebHook';
|
|
33
|
+
// try to find if web hook listener should be included in the list of event listeners
|
|
34
|
+
const webHookIndex = schema.eventListeners.findIndex((item) => {
|
|
35
|
+
return item['@id'] === id;
|
|
36
|
+
});
|
|
37
|
+
if (webHookIndex < 0) {
|
|
38
|
+
// add web hook listener at the end of the list
|
|
39
|
+
schema.eventListeners.push({
|
|
40
|
+
'@id': id,
|
|
41
|
+
type
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
const service = context.application.getService(function WebHookService() {});
|
|
45
|
+
service.logger.info(`A new web hook action with name "${event.target.name || 'unknown'}" has been registered for model "${about}"`);
|
|
46
|
+
// reset model definition
|
|
47
|
+
schemaLoader.setModelDefinition(schema);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return callback();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export {
|
|
55
|
+
afterSave
|
|
56
|
+
}
|