integreat 0.8.0-beta.8 → 0.8.0-rc.0
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/README.md +1292 -938
- package/ava.config.cjs +4 -3
- package/dist/adapters/index.d.ts +4 -0
- package/dist/adapters/index.js +5 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/json.d.ts +6 -0
- package/dist/adapters/json.js +69 -0
- package/dist/adapters/json.js.map +1 -0
- package/dist/authenticators/index.d.ts +2 -2
- package/dist/authenticators/index.js +5 -7
- package/dist/authenticators/index.js.map +1 -1
- package/dist/authenticators/options.d.ts +1 -1
- package/dist/authenticators/options.js +4 -6
- package/dist/authenticators/options.js.map +1 -1
- package/dist/authenticators/token.d.ts +1 -1
- package/dist/authenticators/token.js +1 -3
- package/dist/authenticators/token.js.map +1 -1
- package/dist/close.d.ts +2 -2
- package/dist/close.js +1 -4
- package/dist/close.js.map +1 -1
- package/dist/create.d.ts +13 -10
- package/dist/create.js +45 -33
- package/dist/create.js.map +1 -1
- package/dist/dispatch.d.ts +3 -3
- package/dist/dispatch.js +38 -47
- package/dist/dispatch.js.map +1 -1
- package/dist/dispatchScheduled.d.ts +2 -2
- package/dist/dispatchScheduled.js +1 -3
- package/dist/dispatchScheduled.js.map +1 -1
- package/dist/handlers/delete.d.ts +1 -1
- package/dist/handlers/delete.js +12 -18
- package/dist/handlers/delete.js.map +1 -1
- package/dist/handlers/expire.d.ts +1 -1
- package/dist/handlers/expire.js +18 -29
- package/dist/handlers/expire.js.map +1 -1
- package/dist/handlers/get.d.ts +1 -1
- package/dist/handlers/get.js +27 -29
- package/dist/handlers/get.js.map +1 -1
- package/dist/handlers/getAll.d.ts +2 -2
- package/dist/handlers/getAll.js +21 -24
- package/dist/handlers/getAll.js.map +1 -1
- package/dist/handlers/getIdent.d.ts +1 -1
- package/dist/handlers/getIdent.js +17 -20
- package/dist/handlers/getIdent.js.map +1 -1
- package/dist/handlers/getMeta.d.ts +1 -1
- package/dist/handlers/getMeta.js +18 -26
- package/dist/handlers/getMeta.js.map +1 -1
- package/dist/handlers/index.d.ts +3 -2
- package/dist/handlers/index.js +27 -28
- package/dist/handlers/index.js.map +1 -1
- package/dist/handlers/queue.d.ts +1 -1
- package/dist/handlers/queue.js +11 -16
- package/dist/handlers/queue.js.map +1 -1
- package/dist/handlers/run.d.ts +3 -3
- package/dist/handlers/run.js +164 -109
- package/dist/handlers/run.js.map +1 -1
- package/dist/handlers/service.d.ts +1 -1
- package/dist/handlers/service.js +7 -19
- package/dist/handlers/service.js.map +1 -1
- package/dist/handlers/set.d.ts +1 -1
- package/dist/handlers/set.js +13 -15
- package/dist/handlers/set.js.map +1 -1
- package/dist/handlers/setMeta.d.ts +1 -1
- package/dist/handlers/setMeta.js +10 -13
- package/dist/handlers/setMeta.js.map +1 -1
- package/dist/handlers/sync.d.ts +1 -1
- package/dist/handlers/sync.js +21 -24
- package/dist/handlers/sync.js.map +1 -1
- package/dist/index.d.ts +12 -12
- package/dist/index.js +13 -21
- package/dist/index.js.map +1 -1
- package/dist/listen.d.ts +2 -2
- package/dist/listen.js +2 -5
- package/dist/listen.js.map +1 -1
- package/dist/middleware/completeIdent.d.ts +1 -1
- package/dist/middleware/completeIdent.js +6 -8
- package/dist/middleware/completeIdent.js.map +1 -1
- package/dist/middleware/index.d.ts +1 -1
- package/dist/middleware/index.js +3 -5
- package/dist/middleware/index.js.map +1 -1
- package/dist/mutations/exchangeForm.js +1 -3
- package/dist/mutations/exchangeForm.js.map +1 -1
- package/dist/mutations/exchangeJson.js +1 -3
- package/dist/mutations/exchangeJson.js.map +1 -1
- package/dist/mutations/exchangeUri.js +2 -4
- package/dist/mutations/exchangeUri.js.map +1 -1
- package/dist/mutations/index.js +7 -9
- package/dist/mutations/index.js.map +1 -1
- package/dist/schema/accessForAction.d.ts +1 -1
- package/dist/schema/accessForAction.js +4 -7
- package/dist/schema/accessForAction.js.map +1 -1
- package/dist/schema/createCastMapping.d.ts +3 -3
- package/dist/schema/createCastMapping.js +82 -54
- package/dist/schema/createCastMapping.js.map +1 -1
- package/dist/schema/index.d.ts +4 -4
- package/dist/schema/index.js +29 -25
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/types.d.ts +6 -6
- package/dist/schema/types.js +1 -2
- package/dist/service/Auth.d.ts +4 -3
- package/dist/service/Auth.js +32 -37
- package/dist/service/Auth.js.map +1 -1
- package/dist/service/Connection.d.ts +2 -2
- package/dist/service/Connection.js +19 -35
- package/dist/service/Connection.js.map +1 -1
- package/dist/service/authorize/action.d.ts +2 -2
- package/dist/service/authorize/action.js +5 -7
- package/dist/service/authorize/action.js.map +1 -1
- package/dist/service/authorize/data.d.ts +2 -2
- package/dist/service/authorize/data.js +10 -15
- package/dist/service/authorize/data.js.map +1 -1
- package/dist/service/endpoints/compare.d.ts +1 -1
- package/dist/service/endpoints/compare.js +1 -3
- package/dist/service/endpoints/compare.js.map +1 -1
- package/dist/service/endpoints/create.d.ts +5 -4
- package/dist/service/endpoints/create.js +41 -38
- package/dist/service/endpoints/create.js.map +1 -1
- package/dist/service/endpoints/index.d.ts +6 -6
- package/dist/service/endpoints/index.js +11 -8
- package/dist/service/endpoints/index.js.map +1 -1
- package/dist/service/endpoints/match.d.ts +2 -2
- package/dist/service/endpoints/match.js +5 -8
- package/dist/service/endpoints/match.js.map +1 -1
- package/dist/service/endpoints/types.d.ts +7 -8
- package/dist/service/endpoints/types.js +1 -2
- package/dist/service/index.d.ts +9 -5
- package/dist/service/index.js +152 -67
- package/dist/service/index.js.map +1 -1
- package/dist/service/types.d.ts +17 -19
- package/dist/service/types.js +1 -2
- package/dist/transformers/builtIns/boolean.d.ts +2 -2
- package/dist/transformers/builtIns/boolean.js +3 -5
- package/dist/transformers/builtIns/boolean.js.map +1 -1
- package/dist/transformers/builtIns/date.d.ts +2 -2
- package/dist/transformers/builtIns/date.js +6 -10
- package/dist/transformers/builtIns/date.js.map +1 -1
- package/dist/transformers/builtIns/index.d.ts +3 -11
- package/dist/transformers/builtIns/index.js +18 -19
- package/dist/transformers/builtIns/index.js.map +1 -1
- package/dist/transformers/builtIns/integer.d.ts +2 -2
- package/dist/transformers/builtIns/integer.js +5 -7
- package/dist/transformers/builtIns/integer.js.map +1 -1
- package/dist/transformers/builtIns/number.d.ts +2 -2
- package/dist/transformers/builtIns/number.js +6 -10
- package/dist/transformers/builtIns/number.js.map +1 -1
- package/dist/transformers/builtIns/object.d.ts +2 -2
- package/dist/transformers/builtIns/object.js +5 -7
- package/dist/transformers/builtIns/object.js.map +1 -1
- package/dist/transformers/builtIns/reference.d.ts +2 -2
- package/dist/transformers/builtIns/reference.js +11 -13
- package/dist/transformers/builtIns/reference.js.map +1 -1
- package/dist/transformers/builtIns/string.d.ts +2 -2
- package/dist/transformers/builtIns/string.js +5 -7
- package/dist/transformers/builtIns/string.js.map +1 -1
- package/dist/transformers/builtIns/unarray.d.ts +2 -2
- package/dist/transformers/builtIns/unarray.js +2 -4
- package/dist/transformers/builtIns/unarray.js.map +1 -1
- package/dist/transformers/form.d.ts +2 -2
- package/dist/transformers/form.js +6 -8
- package/dist/transformers/form.js.map +1 -1
- package/dist/transformers/generateUri.d.ts +7 -0
- package/dist/transformers/generateUri.js +69 -0
- package/dist/transformers/generateUri.js.map +1 -0
- package/dist/transformers/index.d.ts +3 -10
- package/dist/transformers/index.js +10 -13
- package/dist/transformers/index.js.map +1 -1
- package/dist/transformers/json.d.ts +2 -2
- package/dist/transformers/json.js +4 -6
- package/dist/transformers/json.js.map +1 -1
- package/dist/transformers/not.d.ts +2 -2
- package/dist/transformers/not.js +1 -3
- package/dist/transformers/not.js.map +1 -1
- package/dist/transformers/trim.d.ts +2 -2
- package/dist/transformers/trim.js +2 -4
- package/dist/transformers/trim.js.map +1 -1
- package/dist/types.d.ts +20 -15
- package/dist/types.js +1 -2
- package/dist/utils/action.d.ts +33 -0
- package/dist/utils/action.js +32 -0
- package/dist/utils/action.js.map +1 -0
- package/dist/utils/array.d.ts +1 -0
- package/dist/utils/array.js +6 -9
- package/dist/utils/array.js.map +1 -1
- package/dist/utils/createAction.d.ts +1 -1
- package/dist/utils/createAction.js +1 -4
- package/dist/utils/createAction.js.map +1 -1
- package/dist/utils/createError.d.ts +3 -3
- package/dist/utils/createError.js +7 -8
- package/dist/utils/createError.js.map +1 -1
- package/dist/utils/createMapOptions.d.ts +4 -4
- package/dist/utils/createMapOptions.js +3 -5
- package/dist/utils/createMapOptions.js.map +1 -1
- package/dist/utils/createSchedule.d.ts +2 -2
- package/dist/utils/createSchedule.js +4 -7
- package/dist/utils/createSchedule.js.map +1 -1
- package/dist/utils/createUnknownServiceError.d.ts +1 -1
- package/dist/utils/createUnknownServiceError.js +4 -7
- package/dist/utils/createUnknownServiceError.js.map +1 -1
- package/dist/utils/deepClone.js +2 -4
- package/dist/utils/deepClone.js.map +1 -1
- package/dist/utils/getField.js +5 -8
- package/dist/utils/getField.js.map +1 -1
- package/dist/utils/getService.d.ts +2 -2
- package/dist/utils/getService.js +1 -4
- package/dist/utils/getService.js.map +1 -1
- package/dist/utils/indexUtils.js +2 -7
- package/dist/utils/indexUtils.js.map +1 -1
- package/dist/utils/is.d.ts +5 -5
- package/dist/utils/is.js +19 -39
- package/dist/utils/is.js.map +1 -1
- package/dist/utils/mappingHelpers.d.ts +1 -1
- package/dist/utils/mappingHelpers.js +5 -12
- package/dist/utils/mappingHelpers.js.map +1 -1
- package/dist/utils/mergeDefinitions.d.ts +1 -1
- package/dist/utils/mergeDefinitions.js +1 -4
- package/dist/utils/mergeDefinitions.js.map +1 -1
- package/dist/utils/mergeResources.d.ts +1 -1
- package/dist/utils/mergeResources.js +1 -4
- package/dist/utils/mergeResources.js.map +1 -1
- package/dist/utils/mutationHelpers.d.ts +2 -0
- package/dist/utils/mutationHelpers.js +33 -0
- package/dist/utils/mutationHelpers.js.map +1 -0
- package/dist/utils/validateFilters.d.ts +1 -2
- package/dist/utils/validateFilters.js +31 -20
- package/dist/utils/validateFilters.js.map +1 -1
- package/dist/utils/xor.d.ts +1 -0
- package/dist/utils/xor.js +4 -0
- package/dist/utils/xor.js.map +1 -0
- package/package.json +23 -20
- package/dist/transformers/formatDate.d.ts +0 -7
- package/dist/transformers/formatDate.js +0 -50
- package/dist/transformers/formatDate.js.map +0 -1
package/README.md
CHANGED
|
@@ -7,64 +7,66 @@ An integration layer for node.js.
|
|
|
7
7
|
[](https://coveralls.io/github/integreat-io/integreat?branch=master)
|
|
8
8
|
[](https://codeclimate.com/github/integreat-io/integreat/maintainability)
|
|
9
9
|
|
|
10
|
-
**Note:** We're
|
|
11
|
-
|
|
12
|
-
highly appreciate feedback, but know that
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
of
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
services
|
|
10
|
+
**Note:** We're closing in on a more stable version, but there might still be
|
|
11
|
+
a few changes coming before v1.0. We encourage trying out and experimenting with
|
|
12
|
+
Integreat, and we highly appreciate feedback, but know that some things might
|
|
13
|
+
still change.
|
|
14
|
+
|
|
15
|
+
The basic idea of Integreat is to make it easy to define how to send data to and
|
|
16
|
+
receive data from a set of [**services**](#services), and expose them through a
|
|
17
|
+
well defined interface, abstracting away the specifics of each service.
|
|
18
|
+
|
|
19
|
+
There are a few concepts that makes this possible:
|
|
20
|
+
|
|
21
|
+
- [**Transporters**](#transporters) and [**adapters**](#adapters) speak the
|
|
22
|
+
language of different types of services and standards of data exchange, and
|
|
23
|
+
does the basic translation to and from the structures used by Integreat. You
|
|
24
|
+
deal with familiar JavasScript objects, arrays, and primitive data types,
|
|
25
|
+
regardless of what the service expects.
|
|
26
|
+
- [**Mutation pipelines**](#mutations) let you define how the data coming from
|
|
27
|
+
or going to a service should be transformed. This includes changing the overal
|
|
28
|
+
structure, renaming properties, transforming and filtering values with
|
|
29
|
+
transformer functions, etc. You may also provide your own transformer
|
|
30
|
+
functions.
|
|
31
|
+
- [**Schemas**](#schemas) serve as a common normalization of data between
|
|
32
|
+
services. You define your own schemas and mutate data to and from them,
|
|
33
|
+
enabling inter-service sharing of data. If you have data in one schema, you
|
|
34
|
+
may send it to any service where you have set up the right mutations for this
|
|
35
|
+
schema, again abstracting away all service details.
|
|
36
|
+
|
|
37
|
+
All configuration is done through basic JSON-friendly structures, and you define
|
|
38
|
+
your services with different endpoints, mutation pipelines, authentication
|
|
39
|
+
schemes, etc.
|
|
40
|
+
|
|
41
|
+
Your configuration is spun up as an Integreat instance. To send and retrieve
|
|
42
|
+
data, you dispatch [**actions**](#actions) to your instance and get
|
|
43
|
+
[**response**](#action-response) objects back. You may define [jobs](#jobs) to
|
|
44
|
+
run simple actions or longer "flows" consisting of several actions with
|
|
45
|
+
conditions and logic. You may also configure [queues](#queues) to have actions
|
|
46
|
+
run in sequence or on a later time.
|
|
33
47
|
|
|
34
48
|
```
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|_________________|
|
|
49
|
+
____________________________________________________
|
|
50
|
+
| |
|
|
51
|
+
| Integreat instance |
|
|
52
|
+
Action ----| | |
|
|
53
|
+
|-> Dispatch <-> Schema <-> Mutation <-> Adapter <-> Transporter <-> Service
|
|
54
|
+
Response <-| | |
|
|
55
|
+
|___________________________________________________|
|
|
43
56
|
```
|
|
44
57
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
Actions that update data on services will reversely map and serialize the data
|
|
50
|
-
before it is sent to a service. These actions may be queued or scheduled, by
|
|
51
|
-
setting up Integreat with the supplied queue middleware.
|
|
52
|
-
|
|
53
|
-
Integreat comes with a [standard data format](#the-data-format), which is the
|
|
54
|
-
only format that will be exposed to the code dispatching the actions. The
|
|
55
|
-
mapping, normalizing, and serializing will happing to and from this format,
|
|
56
|
-
according to the defined schemas and mapping rules.
|
|
57
|
-
|
|
58
|
-
To deal with security and permissions, Integreat has a built-in concept of an
|
|
59
|
-
ident. Other authentication schemes may be mapped to Integreat's ident scheme,
|
|
60
|
-
to provide data security from a service to another service or to the dispatched
|
|
58
|
+
To deal with security and permissions, Integreat has a concept of an ident.
|
|
59
|
+
Other authentication schemes may be mapped to Integreat's ident scheme, to
|
|
60
|
+
provide data security from a service to another service or to the dispatched
|
|
61
61
|
action. A ground principle is that nothing that enters Integreat from an
|
|
62
62
|
authenticated service, will leave Integreat unauthenticated. What this means,
|
|
63
63
|
though, depends on how you define your services.
|
|
64
64
|
|
|
65
|
+
# Usage
|
|
66
|
+
|
|
65
67
|
## Install
|
|
66
68
|
|
|
67
|
-
Requires node
|
|
69
|
+
Requires node v18.
|
|
68
70
|
|
|
69
71
|
Install from npm:
|
|
70
72
|
|
|
@@ -72,802 +74,1066 @@ Install from npm:
|
|
|
72
74
|
npm install integreat
|
|
73
75
|
```
|
|
74
76
|
|
|
75
|
-
|
|
77
|
+
You will probably also need some [transporters](#transporters) and
|
|
78
|
+
[adapters](#adapters), and the basic transformers in
|
|
79
|
+
[`integreat-transformers`](https://github.com/integreat-io/integreat-transformers).
|
|
76
80
|
|
|
77
|
-
|
|
81
|
+
## Basic example
|
|
82
|
+
|
|
83
|
+
The following is the "hello world" example of Integreat. As most hello world
|
|
84
|
+
examples, this is a bit too trivial a use case to demonstrate the real
|
|
85
|
+
usefulness of Integreat, but it shows you the simplest setup possible.
|
|
86
|
+
|
|
87
|
+
Here, we fetch cat facts from the API endpoint
|
|
88
|
+
'https://cat-fact.herokuapp.com/facts', which returns data in JSON and requires
|
|
89
|
+
no authentication. The returned list of facts are mutated and cast to the `fact`
|
|
90
|
+
schema. We only fetch data _from_ the service, and no data is sent _to_ it.
|
|
78
91
|
|
|
79
92
|
```javascript
|
|
80
93
|
import Integreat from 'integreat'
|
|
81
94
|
import httpTransporter from 'integreat-transporter-http'
|
|
95
|
+
import jsonAdapter from 'integreat-adapter-json'
|
|
82
96
|
|
|
83
97
|
const schemas = [
|
|
84
98
|
{
|
|
85
|
-
id: '
|
|
86
|
-
|
|
87
|
-
|
|
99
|
+
id: 'fact', // The id of the schema
|
|
100
|
+
shape: {
|
|
101
|
+
// The fields of the type
|
|
102
|
+
id: 'string', // An id field will always be included, but we define it here for readability
|
|
103
|
+
text: 'string', // The text of the cat fact
|
|
104
|
+
createdAt: 'date', // The created date (`createdAt` and `updatedAt` will always be dates)
|
|
105
|
+
},
|
|
106
|
+
access: { allow: 'all' }, // No access restrictions
|
|
88
107
|
},
|
|
89
108
|
]
|
|
90
109
|
|
|
91
110
|
const services = [
|
|
92
111
|
{
|
|
93
|
-
id: '
|
|
94
|
-
transporter: 'http',
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
112
|
+
id: 'catfact', // The id of the service
|
|
113
|
+
transporter: 'http', // Use the http transporter
|
|
114
|
+
adapters: ['json'], // Run the request and the response through the json adapter
|
|
115
|
+
options: {
|
|
116
|
+
// Options for the transporter
|
|
117
|
+
uri: 'https://cat-fact.herokuapp.com/facts', // Only the uri is needed here
|
|
98
118
|
},
|
|
119
|
+
endpoints: [
|
|
120
|
+
{
|
|
121
|
+
match: { action: 'GET', type: 'fact' }, // Match to a GET action for type 'fact'
|
|
122
|
+
mutation: {
|
|
123
|
+
$direction: 'from', // We're mutating data _from_ the service
|
|
124
|
+
// Here we're mutating `response.data` and "setting it back" where we found it ...
|
|
125
|
+
'response.data': [
|
|
126
|
+
'response.data[]',
|
|
127
|
+
{
|
|
128
|
+
$iterate: true, // Mutate each item in an array
|
|
129
|
+
id: '_id', // The id is called `_id` the data from the service
|
|
130
|
+
text: 'text', // text is called `text`
|
|
131
|
+
createdAt: 'createdAt', // Creation date is called `createdAt`
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
],
|
|
99
137
|
},
|
|
100
138
|
]
|
|
101
139
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const great = Integreat.create(
|
|
105
|
-
|
|
106
|
-
|
|
140
|
+
// Create the Integreat instance from our definitions and provide the
|
|
141
|
+
// transporters and adapters we require.
|
|
142
|
+
const great = Integreat.create(
|
|
143
|
+
{ schemas, services },
|
|
144
|
+
{ transporters: { http: httpTransporter }, adapters: { json: jsonAdapter } }
|
|
145
|
+
)
|
|
107
146
|
|
|
108
|
-
|
|
109
|
-
|
|
147
|
+
// Prepare an action to fetch all cat facts from the service `catfact`
|
|
148
|
+
const action = { type: 'GET', payload: { type: 'fact', service: 'catfact' } }
|
|
110
149
|
|
|
111
|
-
|
|
150
|
+
// Dispatch the action and get the response
|
|
151
|
+
const response = await great.dispatch(action)
|
|
112
152
|
```
|
|
113
153
|
|
|
114
|
-
|
|
115
|
-
demonstrate the real usefulness of Integreat, but shows the simplest setup
|
|
116
|
-
possible.
|
|
117
|
-
|
|
118
|
-
The example requires an imagined api at 'https://api.helloworld.io/json',
|
|
119
|
-
returning the following json data:
|
|
154
|
+
The `response` object will look like this:
|
|
120
155
|
|
|
121
|
-
```
|
|
122
|
-
{
|
|
123
|
-
"message": "Hello world"
|
|
124
|
-
}
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## Schema definitions
|
|
128
|
-
|
|
129
|
-
To do anything with Integreat, you need to define one or more schemas. They
|
|
130
|
-
describe the data you expected to get out of Integreat. A type will be
|
|
131
|
-
associated with a service, which is used to retrieve data for the type, unless
|
|
132
|
-
another service is specified.
|
|
133
|
-
|
|
134
|
-
```
|
|
156
|
+
```javascript
|
|
135
157
|
{
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
<relId>: {
|
|
147
|
-
type: <string>,
|
|
148
|
-
default: <object>,
|
|
149
|
-
query: <query params>
|
|
150
|
-
}
|
|
151
|
-
},
|
|
152
|
-
auth: <auth def>
|
|
158
|
+
status: 'ok',
|
|
159
|
+
data: [
|
|
160
|
+
{
|
|
161
|
+
id: '58e008780aac31001185ed05',
|
|
162
|
+
$type: 'fact',
|
|
163
|
+
text: 'Owning a cat can reduce the risk of stroke and heart attack by a third.',
|
|
164
|
+
createdAt: new Date('2018-03-29T20:20:03.844Z')
|
|
165
|
+
},
|
|
166
|
+
// ...
|
|
167
|
+
]
|
|
153
168
|
}
|
|
154
169
|
```
|
|
155
170
|
|
|
156
|
-
|
|
157
|
-
optional, but it's good practice to set it to the plural mode of the `id`, as
|
|
158
|
-
some interfaces may use it. For instance,
|
|
159
|
-
[`integreat-api-json`](https://github.com/integreat-io/integreat-api-json) uses
|
|
160
|
-
it to build a RESTful endpoint structure, and will append an _s_ to `id` if
|
|
161
|
-
`plural` is not set – which may be weird in some cases.
|
|
162
|
-
|
|
163
|
-
### Attributes
|
|
164
|
-
|
|
165
|
-
Each attribute is defined with an id, which may contain only alphanumeric
|
|
166
|
-
characters, and may not start with a digit. This id is used to reference the
|
|
167
|
-
attribute.
|
|
168
|
-
|
|
169
|
-
The `type` defaults to `string`. Other options are `integer`, `float`,
|
|
170
|
-
`boolean`, and `date`. Data from Integreat will be cast to corresponding
|
|
171
|
-
JavaScript types.
|
|
172
|
-
|
|
173
|
-
The `default` value will be used when a data service does not provide this value.
|
|
174
|
-
Default is `null`.
|
|
171
|
+
# Integreat concepts
|
|
175
172
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
Relationship is defined in the same way as attributes, but with one important
|
|
179
|
-
difference: The `type` property refers to other Integreat schemas. E.g. a
|
|
180
|
-
schema for an article may have a relationship called `author`, with
|
|
181
|
-
`type: 'user'`, referring to the schema with id `user`. `type` is required on
|
|
182
|
-
relationships.
|
|
183
|
-
|
|
184
|
-
The `default` property sets a default value for the relationship, in the same
|
|
185
|
-
way as for attributes, but note that this value should be a valid id for an item
|
|
186
|
-
of the type the relationship refers to.
|
|
187
|
-
|
|
188
|
-
Finally, relationships have a `query` property, which is used to retrieve items
|
|
189
|
-
for this relationship. In many cases, a service may not have data that maps to
|
|
190
|
-
id(s) for a relationship directly, and this is the typical use case for this
|
|
191
|
-
property.
|
|
192
|
-
|
|
193
|
-
The `query` property is an object with key/value pairs, where the key is the id
|
|
194
|
-
of a field (an attribute, a relationship, or `id`) on the schema the relationship
|
|
195
|
-
refers to, and the value is the id of field on this schema.
|
|
196
|
-
|
|
197
|
-
Example schema with a query definition:
|
|
198
|
-
|
|
199
|
-
```
|
|
200
|
-
{
|
|
201
|
-
id: 'user',
|
|
202
|
-
...
|
|
203
|
-
relationships: {
|
|
204
|
-
articles: {type: 'article', query: {author: 'id'}}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
```
|
|
173
|
+
As mentioned in the introduction, the building blocks of Integreat are services,
|
|
174
|
+
transporters and adapters, mutation pipelines, and schemas.
|
|
208
175
|
|
|
209
|
-
|
|
210
|
-
querying for all items of type `article`, where the `author` field equals the
|
|
211
|
-
`id` of the `user` item in question.
|
|
176
|
+
## Services
|
|
212
177
|
|
|
213
|
-
|
|
178
|
+
A service is the API, database, FTP server, queue, etc. that you want to get
|
|
179
|
+
data from and/or set data to. We pass on a set of service definitions to
|
|
180
|
+
Integreat, specifying what transporter, adapters, authentication schemas it
|
|
181
|
+
requires, in adition to defining the different endpoints available on the
|
|
182
|
+
service, how they should be called, and how data should be mutated in each case.
|
|
214
183
|
|
|
215
|
-
|
|
216
|
-
|
|
184
|
+
We'll get back to the details of all of this in turn, but first we want to
|
|
185
|
+
highlight how central the concept of a service is to Integreat. Basically, in
|
|
186
|
+
Integreat "everything is a service". A simple REST/JSON API is a service, a
|
|
187
|
+
database is a service, and everything external you want to communicate with are
|
|
188
|
+
services. Want to set up a queue to handle actions one by one? That's a service.
|
|
189
|
+
Want to cache data in a memory store? That's a service. Want to schedule actions
|
|
190
|
+
to run on intervals? That's a service to. By simply defining services and their
|
|
191
|
+
specifics, you may set up a variety of different types of configurations with
|
|
192
|
+
the same few building blocks. This is very powerful as soon as you get into the
|
|
193
|
+
right mindset.
|
|
217
194
|
|
|
218
|
-
|
|
219
|
-
|
|
195
|
+
Services are configured by service definitions, and tells Integreat how to
|
|
196
|
+
fetch data from a service, how to mutate this data to schemas, and how to send
|
|
197
|
+
data back to the service.
|
|
220
198
|
|
|
221
|
-
|
|
199
|
+
The service definition object includes the transporter id, adapter ids, any
|
|
200
|
+
authentication method, the endpoints for fetching from and sending to the
|
|
201
|
+
service, mutations that data to all endpoints will pass through, and options
|
|
202
|
+
for transporters, adapters, etc.
|
|
222
203
|
|
|
223
204
|
```javascript
|
|
224
205
|
{
|
|
225
|
-
id:
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
access: 'auth'
|
|
229
|
-
}
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
To signal that the schema really has no need for authorization, use `all`.
|
|
233
|
-
This is not the same as not setting the `auth` prop, as `all` will override
|
|
234
|
-
Integreat's principle of not letting authorized data out of Integreat without
|
|
235
|
-
an authorization rule. In a way, you can say that `all` is an authorization
|
|
236
|
-
rule, but it allows anybody to access the data, even the unauthenticated.
|
|
237
|
-
|
|
238
|
-
The last of the simpler access types, is `none`, which will simly give no one
|
|
239
|
-
access, no matter who they are.
|
|
240
|
-
|
|
241
|
-
For a more fine-grained rules, set `access` to an access definition.
|
|
242
|
-
|
|
243
|
-
## Service definitions
|
|
244
|
-
|
|
245
|
-
Service definitions are at the core of Integreat, as they define the services to
|
|
246
|
-
fetch data from, how to map this data to a set of items to make available
|
|
247
|
-
through Integreat's data api, and how to send data back to the service.
|
|
248
|
-
|
|
249
|
-
A service definition object defines the adapter, any authentication method, the
|
|
250
|
-
endpoints for fetching from and sending to the service, and mappings to the
|
|
251
|
-
supported schemas (attributes and relationships):
|
|
252
|
-
|
|
253
|
-
```
|
|
254
|
-
{
|
|
255
|
-
id: <string>,
|
|
256
|
-
adapter: <string>,
|
|
206
|
+
id: <service id>,
|
|
207
|
+
transporter: <transporter id>,
|
|
208
|
+
adapters: [<adapter id>, <adapter id>, ...],
|
|
257
209
|
auth: <auth id>,
|
|
258
210
|
meta: <type id>,
|
|
259
211
|
options: {...},
|
|
212
|
+
mutation: <mutation pipeline>,
|
|
260
213
|
endpoints: [
|
|
261
214
|
<endpoint definition>,
|
|
262
215
|
...
|
|
263
|
-
]
|
|
264
|
-
mappings: {
|
|
265
|
-
<schema id>: <mapping definition | mapping id>,
|
|
266
|
-
...
|
|
267
|
-
}
|
|
216
|
+
]
|
|
268
217
|
}
|
|
269
218
|
```
|
|
270
219
|
|
|
271
220
|
Service definitions are passed to Integreat on creation through the
|
|
272
|
-
`Integreat.create()` function. There is no way to change
|
|
273
|
-
creation.
|
|
221
|
+
`Integreat.create()` function. There is no way to change service defintions
|
|
222
|
+
after creation.
|
|
274
223
|
|
|
275
|
-
See [
|
|
276
|
-
|
|
224
|
+
See [mutations](#mutations) for a description of how to define the mutation
|
|
225
|
+
pipeline for a service.
|
|
277
226
|
|
|
278
227
|
The `auth` property should normally be set to the id of an
|
|
279
|
-
[auth definition](#service-authentication) if the service requires
|
|
280
|
-
In cases where the service is authenticated by other means, e.g.
|
|
281
|
-
username and password in the uri, set the `auth` property to `true`
|
|
282
|
-
that this is an authenticated service.
|
|
283
|
-
|
|
284
|
-
###
|
|
228
|
+
[auth definition](#service-authentication), if the service requires
|
|
229
|
+
authentication. In cases where the service is authenticated by other means, e.g.
|
|
230
|
+
by including username and password in the uri, set the `auth` property to `true`
|
|
231
|
+
to signal that this is an authenticated service.
|
|
232
|
+
|
|
233
|
+
### Endpoints
|
|
234
|
+
|
|
235
|
+
A service will have at least one endpoint, but often there will be several. An
|
|
236
|
+
endpoint is a definition of one of the ways Integreat may interact with a
|
|
237
|
+
service. You decide how you want to set up the endpoints and what is the right
|
|
238
|
+
"endpoint design" for a service, but there might be one endpoint for each
|
|
239
|
+
operation that can be done with a type of data.
|
|
240
|
+
|
|
241
|
+
For example, let's say you have a simple REST API with blog articles and
|
|
242
|
+
authors. There will most likely be an endpoint to fetch all (or some) articles,
|
|
243
|
+
one endpoint for fetching one article by id, one endpoint for creating an
|
|
244
|
+
article, one for updating an article, and so on. And you'll have similar
|
|
245
|
+
endpoints for authors, one endpoint for fetching all, one for fetching one by
|
|
246
|
+
id, one endpoint for creating an author, etc. As this is REST, each endpoint
|
|
247
|
+
will address a different combination of urls and http verbs (through the
|
|
248
|
+
transporter).
|
|
249
|
+
|
|
250
|
+
As another example, you may be accessing a database of articles and authors
|
|
251
|
+
directly. The configuration details will be very different than for a REST API,
|
|
252
|
+
but you'll probably have the same endpoints, fetching all articles, fetching
|
|
253
|
+
one, creating, updating, and the same all over for users. Instead of urls and
|
|
254
|
+
http verbs, as for REST, these endpoints will address different databases and
|
|
255
|
+
different database operations (through the transporter).
|
|
256
|
+
|
|
257
|
+
> Note: This is not to say that Integreat requires you to set up endpoints
|
|
258
|
+
> exactly as described in these examples, it might be that you would like to set
|
|
259
|
+
> up an endpoint that handles many of these cases. The intention here is just to
|
|
260
|
+
> give you an understanding of what an endpoint is in Integreat.
|
|
261
|
+
|
|
262
|
+
When you dispatch an action, Integreat will figure out what service and what
|
|
263
|
+
endpoint to send the action to. The target service is often specified in the
|
|
264
|
+
action payload with the `service` property, but if not, the default service of
|
|
265
|
+
the schema specified with the payload `type` property, will be used.
|
|
266
|
+
|
|
267
|
+
The matching to an endpoint is done by finding the endpoint whose `match` object
|
|
268
|
+
matches the action with most accuracy. The rules of the endpoint matching is
|
|
269
|
+
describe in more details [below](#match-properties).
|
|
270
|
+
|
|
271
|
+
Here's the format of an endpoint definition:
|
|
285
272
|
|
|
286
|
-
```
|
|
273
|
+
```javascript
|
|
287
274
|
{
|
|
288
|
-
id: <
|
|
275
|
+
id: <endpoint id>,
|
|
289
276
|
match: {
|
|
290
|
-
type: <
|
|
291
|
-
scope: <'collection'|'member'>,
|
|
277
|
+
type: <schema id>,
|
|
278
|
+
scope: <'collection'|'member'|'members'|'all'>,
|
|
292
279
|
action: <action type>,
|
|
293
280
|
params: {...},
|
|
294
|
-
|
|
281
|
+
incoming: <boolean>,
|
|
282
|
+
filters: {...}
|
|
295
283
|
},
|
|
296
|
-
validate: [],
|
|
297
284
|
mutation: <mutation pipeline>,
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
<schema id>: <mapping definition | mapping id>,
|
|
302
|
-
},
|
|
303
|
-
options: {...}
|
|
285
|
+
options: {...},
|
|
286
|
+
allowRawRequest: <boolean>,
|
|
287
|
+
allowRawResponse: <boolean>
|
|
304
288
|
}
|
|
305
289
|
```
|
|
306
290
|
|
|
291
|
+
All of these properties are optional. An empty endpoint defintion object will
|
|
292
|
+
match anything, pass on the action to the transporter untouched, and relay any
|
|
293
|
+
response coming back. This might be what you need, but often you'll want to
|
|
294
|
+
specify a few things:
|
|
295
|
+
|
|
296
|
+
- `id`: The endpoint may have an id, which you may use to specify that you want
|
|
297
|
+
an action to go to this particular id. However, most of the time you'll set
|
|
298
|
+
up the `match` object so that Integreat will decide what endpoint to use for
|
|
299
|
+
the action you dispatch.
|
|
300
|
+
- `match`: The match object is used to decide the right endpoint for an action.
|
|
301
|
+
More one this in the [Match properties](#match-properties) section.
|
|
302
|
+
- `mutation`: A mutation pipeline for the endpoint. The pipeline is run for both
|
|
303
|
+
actions going to a service and the response coming back, so keep this in mind
|
|
304
|
+
when you set up this pipeline. See [Mutation pipelines](#mutation-pipelines)
|
|
305
|
+
for more on how to define the mutation.
|
|
306
|
+
- `options`: This object will be passed on to the transporter and may contain
|
|
307
|
+
any properties that are meaningful to the transporter. You may also add other
|
|
308
|
+
properties for use in your mutations, but keep in mind that they will be sent
|
|
309
|
+
to the transporter. The endpoint `options` object is merged with the service
|
|
310
|
+
`options` object. Endpoint properties will override equally named service
|
|
311
|
+
properties, but this is done through deep merging, so child objects will be
|
|
312
|
+
merged as well.
|
|
313
|
+
- `allowRawRequest`: When set to `true`, payload `data` sent to this endpoint
|
|
314
|
+
will not by cast automatically nor will an error be returned if the data is
|
|
315
|
+
not typed.
|
|
316
|
+
- `allowRawResponse`: When set to `true`, response `data` coming from this
|
|
317
|
+
endpoint will not by cast automatically nor will an error be returned if the
|
|
318
|
+
data is not typed.
|
|
319
|
+
|
|
307
320
|
#### Match properties
|
|
308
321
|
|
|
309
322
|
An endpoint may specify none or more of the following match properties:
|
|
310
323
|
|
|
311
|
-
- `id`: An action payload may include an `endpoint` property, that will be
|
|
312
|
-
matched against this `id`. For actions with an endpoint id, no other matching
|
|
313
|
-
properties will be considered
|
|
314
324
|
- `type`: When set, the endpoint will only be used for actions with the
|
|
315
|
-
specified schema type (
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
325
|
+
specified schema type (the schema's id). `type` may also be an array of types,
|
|
326
|
+
matching any one of the schemas in the list.
|
|
327
|
+
- `scope`: May be `member`, `members`, `collection`, or `all`, to specify that
|
|
328
|
+
the endpoint should be used to request one item (member) by id, several items
|
|
329
|
+
by ids (members), or an entire collection of items. Setting this to `member`
|
|
330
|
+
or `members` will only match actions with a payload `id` property, and the
|
|
331
|
+
`id` should be an array of ids for `members`. Not setting this property, or
|
|
332
|
+
setting it to `all`, signals an endpoint that will work for all scopes.
|
|
320
333
|
- `action`: May be set to the type string of an action. The endpoint will match
|
|
321
|
-
only actions of this type
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
`
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
334
|
+
only actions of this type. When this is not specified, any action type will
|
|
335
|
+
match. `action` may also be a list of action types, matching any of these.
|
|
336
|
+
- `params`: This object should list all params that this endpoint supports. A
|
|
337
|
+
param in this context is any property on the action payload except `type`,
|
|
338
|
+
`id`, or `data`. Use the param name as key on this object and set the value to
|
|
339
|
+
`true` if it is required, and `false` if it is optional. When matching
|
|
340
|
+
endpoints, an action will only match if it has all the required params, and in
|
|
341
|
+
case several match, the endpoint with more specified params will be preferred.
|
|
342
|
+
- `incoming`: If this is `true`, it will only match incoming actions, if `false`
|
|
343
|
+
only outgoing, and if not set, it will match both.
|
|
344
|
+
- `filters`: The filter object specifies a set of tests that needs to match an
|
|
345
|
+
action. The key of the object is a full dot notation path for the action
|
|
346
|
+
object, e.g. `payload.onlyArchived` and the value is a
|
|
347
|
+
[JSON Schema Validation object](https://json-schema.org/draft/2020-12/json-schema-validation.html).
|
|
348
|
+
|
|
349
|
+
> Editor's note: Describe what incoming actions are, and give more details on
|
|
350
|
+
> filters.
|
|
351
|
+
|
|
352
|
+
There might be cases where several endpoints match an action, and in these cases
|
|
353
|
+
the endpoint with the highest level of specificity will be used. E.g., for a
|
|
354
|
+
`GET` action asking for resources of type `entry`, an endpoint with both
|
|
355
|
+
`action: 'GET'` and `type: 'entry'` is picked over an endpoint matching all
|
|
356
|
+
`GET` actions regardless of type. For `params` and `filters` this is decided by
|
|
357
|
+
the highest number of properties on these objects.
|
|
358
|
+
|
|
359
|
+
The order of the endpoints in the `endpoints` list matters only when two
|
|
360
|
+
endpoints are equally specified with the same match properties specified. Then
|
|
361
|
+
the first one is used.
|
|
337
362
|
|
|
338
363
|
When no match properties are set, the endpoint will match any actions, as long
|
|
339
364
|
as no other endpoints match.
|
|
340
365
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
`params` object, with the value set to `true` for required params. All
|
|
345
|
-
properties are treated as strings.
|
|
366
|
+
Finally, if an action specifies the endpoint id with the `endpoint`
|
|
367
|
+
[payload property](#payload-properties), this overrides all else, and the
|
|
368
|
+
endpoint with the id is used regardless of how the match object would apply.
|
|
346
369
|
|
|
347
|
-
|
|
348
|
-
present.
|
|
370
|
+
Example service definition with endpoint match object:
|
|
349
371
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
```
|
|
372
|
+
```javascript
|
|
353
373
|
{
|
|
354
374
|
id: 'entries',
|
|
355
|
-
|
|
375
|
+
transporter: 'http',
|
|
356
376
|
endpoints: [
|
|
357
377
|
{
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
378
|
+
match: {
|
|
379
|
+
type: 'entry',
|
|
380
|
+
action: 'GET',
|
|
381
|
+
scope: 'collection',
|
|
382
|
+
params: {
|
|
383
|
+
author: true,
|
|
384
|
+
archive: false
|
|
385
|
+
}
|
|
361
386
|
},
|
|
362
387
|
options: {
|
|
363
|
-
uri: 'https://example.api.com/1.0/{author}/{type}_log
|
|
388
|
+
uri: 'https://example.api.com/1.0/{author}/{type}_log?archive={archive}'
|
|
364
389
|
}
|
|
365
390
|
}
|
|
366
391
|
],
|
|
367
|
-
...
|
|
392
|
+
// ...
|
|
368
393
|
}
|
|
369
394
|
```
|
|
370
395
|
|
|
371
|
-
|
|
372
|
-
unless to define them as required:
|
|
373
|
-
|
|
374
|
-
- `id`: The item id from the action payload or from the data property (if it is
|
|
375
|
-
an object and not an array). Required in endpoints with `scope: 'member'`, not
|
|
376
|
-
included for `scope: 'collection'`, and optional when scope is not set.
|
|
377
|
-
- `type`: The item type from the action payload or from the data property (if it
|
|
378
|
-
is an object and not an array).
|
|
379
|
-
- `typePlural`: The plural form of the type, gotten from the corresponding
|
|
380
|
-
schema's `plural` prop – or by adding an 's' to the type is `plural` is not
|
|
381
|
-
set.
|
|
382
|
-
|
|
383
|
-
#### Options property
|
|
396
|
+
### Service authentication
|
|
384
397
|
|
|
385
|
-
|
|
386
|
-
an object with properties to be passed to the adapter as part of a request. The
|
|
387
|
-
props are completely adapter specific, so that each adapter can dictate what
|
|
388
|
-
kind of information it will need, but there are a set of recommended props to
|
|
389
|
-
use when they are relevant:
|
|
390
|
-
|
|
391
|
-
- `uri`: A uri template, where e.g. `{id}` will be placed with the value of the
|
|
392
|
-
parameter `id` from the action payload. For a full specification of the template
|
|
393
|
-
format, see
|
|
394
|
-
[Integreat URI Template](https://github.com/integreat-io/great-uri-template).
|
|
395
|
-
|
|
396
|
-
- `path`: A [path](#paths) into the data, specific for this endpoint. It will
|
|
397
|
-
usually point to an array, in which the items can be found, but as mappings may
|
|
398
|
-
have their own `path`, the endpoint path may point to an object from where the
|
|
399
|
-
different mapping paths point to different arrays.
|
|
400
|
-
|
|
401
|
-
- `method`: An adapter specific keyword, to tell the adapter which method of
|
|
402
|
-
transportation to use. For adapters based on http, the options will typically
|
|
403
|
-
be `PUT`, `POST`, etc. The method specified on the endpoint will override any
|
|
404
|
-
method provided elsewhere. As an example, the `SET` action will use the `PUT`
|
|
405
|
-
method as default, but only if no method is specified on the endpoint.
|
|
406
|
-
|
|
407
|
-
## Mapping definition
|
|
398
|
+
This definition format is used to authenticate with a service:
|
|
408
399
|
|
|
409
|
-
```
|
|
400
|
+
```javascript
|
|
410
401
|
{
|
|
411
|
-
id: <
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
<attrKey>: {
|
|
416
|
-
path: <string>,
|
|
417
|
-
transform: <transform pipeline>
|
|
418
|
-
}
|
|
419
|
-
},
|
|
420
|
-
relationships: {
|
|
421
|
-
<relKey>: {
|
|
422
|
-
path: <string>,
|
|
423
|
-
transform: <transform pipeline>
|
|
424
|
-
}
|
|
425
|
-
},
|
|
426
|
-
toService: {
|
|
427
|
-
path: <string>,
|
|
428
|
-
transform: <transform pipeline>
|
|
402
|
+
id: <id>,
|
|
403
|
+
authenticator: <authenticator id>,
|
|
404
|
+
options: {
|
|
405
|
+
// ...
|
|
429
406
|
}
|
|
430
|
-
qualifier: <string>,
|
|
431
|
-
transform: <transform pipeline>,
|
|
432
|
-
filterFrom: <filter pipeline>,
|
|
433
|
-
filterTo: <filter pipeline>
|
|
434
407
|
}
|
|
435
408
|
```
|
|
436
409
|
|
|
437
|
-
`id
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
point to the right value for each attribute and relationship. These values will
|
|
445
|
-
be cast to the right schema after all mapping, mutating, and transforming is
|
|
446
|
-
done. The value of each attribute or relationship should be in a format that can
|
|
447
|
-
be coerced to the type defined in the schema. The `transform` pipeline may be
|
|
448
|
-
used to accomplish this, but it is sufficient to return something that can be
|
|
449
|
-
cast to the right type. E.g. returning `'3'` for an integer is okay, as
|
|
450
|
-
Integreat will cast it with `parseInt()`.
|
|
451
|
-
|
|
452
|
-
The `param` property is an alternative to specifying a `path`, and refers to a
|
|
453
|
-
param passed to the `retreive` method. Instead of retrieving a value from the
|
|
454
|
-
service data, an attribute or relationship with `param` will get its value from
|
|
455
|
-
the corresponding parameter. When sending data _to_ a service, this
|
|
456
|
-
attribute/relationship will be disregarded.
|
|
457
|
-
|
|
458
|
-
Most of the time, your `attributes` and `relationships` definitions will only
|
|
459
|
-
have the `path` property, so providing the `path` string instead of an object
|
|
460
|
-
is a useful shorthand for this. I.e. `{title: 'article.headline'}` translates to
|
|
461
|
-
`{title: {path: 'article.headline'}}`.
|
|
462
|
-
|
|
463
|
-
The `toService` section behaves just like `attributes` and `relationships`,
|
|
464
|
-
except that it is only used when mapping _to_ a service. This is where you'll
|
|
465
|
-
put mappings that don't have a corresponding field in the schema you map
|
|
466
|
-
to/from, e.g. root paths like `^params.service`.
|
|
467
|
-
|
|
468
|
-
Mappings relate to both services and schemas, as the thing that binds them
|
|
469
|
-
together, but it is the service definition that "owns" them. The `mappings`
|
|
470
|
-
object on a service definition contains the ids of all relevant schemas as keys,
|
|
471
|
-
and the value for these keys are either a mapping definition object or a mapping
|
|
472
|
-
id. The `type` property of a mapping definition is optional when defined
|
|
473
|
-
directly in a service definition, but should match the key if it is set.
|
|
474
|
-
|
|
475
|
-
In some cases you may be able to reuse a mapping for several services or several
|
|
476
|
-
types, by referring to the mapping id from several service definitions.
|
|
477
|
-
|
|
478
|
-
### Paths
|
|
479
|
-
|
|
480
|
-
Mappings, attributes, and relationships all have an optional `path` property,
|
|
481
|
-
for specifying what part of the data from the service to return in each case.
|
|
482
|
-
(Endpoints may also have a `path` property, but not all adapters support this.)
|
|
483
|
-
|
|
484
|
-
The `path` properties use a dot notation with array brackets.
|
|
485
|
-
|
|
486
|
-
For example, with this data returned from the service ...
|
|
410
|
+
- `id`: The id used to reference this authentication, especially from the
|
|
411
|
+
[service definition](#services).
|
|
412
|
+
- `authenticator`: The id of an authenticator used to authenticate requests.
|
|
413
|
+
Integreat comes with a few basic ones built in, and there are others
|
|
414
|
+
available.
|
|
415
|
+
- `options`: An object of values meaningful to the authenticator. See the
|
|
416
|
+
documentation of each authenticator to learn how it should be configured.
|
|
487
417
|
|
|
488
|
-
|
|
489
|
-
const data = {
|
|
490
|
-
sections: [
|
|
491
|
-
{
|
|
492
|
-
title: 'First section',
|
|
493
|
-
articles: {
|
|
494
|
-
items: [
|
|
495
|
-
{id: 'article1', title: 'The title', body: {content: 'The text'}}
|
|
496
|
-
],
|
|
497
|
-
...
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
]
|
|
501
|
-
}
|
|
502
|
-
```
|
|
418
|
+
Available authenticators:
|
|
503
419
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
420
|
+
- `options`: Will pass on the options as authentication, so whatever you provide
|
|
421
|
+
here is the authentication. What options to provide, then, is depending on
|
|
422
|
+
what the relevant transporter requires. This is built into Integreat.
|
|
423
|
+
- `token`: A simple way of authenticating with a given token. For HTTP requests,
|
|
424
|
+
the token will be provided as a `Authorization` header, and a configurable
|
|
425
|
+
prefix like `Basic` or `Bearer`. This is built into Integreat.
|
|
426
|
+
- [`jwt`](https://github.com/integreat-io/integreat-authenticator-jwt): Will
|
|
427
|
+
generate and encode a JavaScript Web Token (JWT) based on the options.
|
|
428
|
+
- [`oauth2`](https://github.com/integreat-io/integreat-authenticator-oauth2):
|
|
429
|
+
Will run the balett of calling different OAuth2 endpoints and receive a token
|
|
430
|
+
based on the provided options.
|
|
507
431
|
|
|
508
|
-
|
|
432
|
+
### Configuring service metadata
|
|
509
433
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
- `[1:3]` - Matches all items from index 1 to 3, not including 3.
|
|
513
|
-
- `[-1]` - Matches the last item in the array.
|
|
514
|
-
- `[id="ent1"]` - Matches the item with an id equal to `'ent1'`
|
|
434
|
+
Integreat supports getting and setting metadata for a service. The most common
|
|
435
|
+
use of this is to keep track of when data of a certain type was last synced.
|
|
515
436
|
|
|
516
|
-
|
|
437
|
+
Some services may have support for storing their own metadata, but usually you
|
|
438
|
+
set up a dedicated service for storing other services' metadata. A few different
|
|
439
|
+
pieces goes into setting up a meta store:
|
|
517
440
|
|
|
518
|
-
-
|
|
519
|
-
-
|
|
441
|
+
- A meta schema with the fields available as metadata
|
|
442
|
+
- A service for storing metadata, with an endpoint suporting the metadata schema
|
|
443
|
+
- Possible a metadata mutation for the metadata service
|
|
520
444
|
|
|
521
|
-
When
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
445
|
+
When all of this is set up, you activate the metadata on the service the
|
|
446
|
+
metadata will be stored for, by setting the `meta` property to the id of the
|
|
447
|
+
schema defining the metadata fields. The `service` set on the schema will tell
|
|
448
|
+
Integreat what service to get and set the metadata from/to.
|
|
525
449
|
|
|
526
|
-
|
|
527
|
-
single, non-negative index is specified in the path.
|
|
450
|
+
The schema will look something like this:
|
|
528
451
|
|
|
529
|
-
|
|
530
|
-
|
|
452
|
+
```javascript
|
|
453
|
+
{
|
|
454
|
+
id: 'meta', // You may give it any id you'd like and reference it on the `meta` prop on the service
|
|
455
|
+
service: <id of service handling the metadata>,
|
|
456
|
+
shape: {
|
|
457
|
+
<metadataKey>: <type string>,
|
|
458
|
+
// ...
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
```
|
|
531
462
|
|
|
532
|
-
|
|
533
|
-
the
|
|
534
|
-
|
|
463
|
+
To get or set metadata, use [`GET_META`](#get_meta) and [`SET_META`](#set_meta)
|
|
464
|
+
with the service you are getting metadata from as the `service`. Integreat will
|
|
465
|
+
figure out the rest.
|
|
535
466
|
|
|
536
|
-
|
|
467
|
+
## Transporters
|
|
537
468
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
the
|
|
469
|
+
A transporter handles all the details of sending and receiving data to and from
|
|
470
|
+
a service. When dispatching an action to a service, the action will be handled
|
|
471
|
+
in a relevant manner for the type of service the transporter supports, e.g.
|
|
472
|
+
sending an http requrest for the HTTP transporter, or doing a query to a
|
|
473
|
+
database for the MongoDb transporter. Some transporters may also support
|
|
474
|
+
listening to a service, e.g. the HTTP transporter listing for incoming requests
|
|
475
|
+
or the MQTT transporter subscribing to events on a topic.
|
|
544
476
|
|
|
545
|
-
|
|
546
|
-
false. If a mapping has qualifiers, it will only be applied to data that
|
|
547
|
-
satisfies all its qualifiers. Qualifiers are applied to the data at the
|
|
548
|
-
mapping's path, before it is mapped and transformed.
|
|
477
|
+
Integreat has transporters for some common cases, and more may come:
|
|
549
478
|
|
|
550
|
-
|
|
479
|
+
- [Bull](https://github.com/integreat-io/integreat-transporter-bull)
|
|
480
|
+
- [FTP](https://github.com/integreat-io/integreat-transporter-ftp)
|
|
481
|
+
- [HTTP](https://github.com/integreat-io/integreat-transporter-http)
|
|
482
|
+
- [MongoDb](https://github.com/integreat-io/integreat-transporter-mongodb)
|
|
483
|
+
- [MQTT](https://github.com/integreat-io/integreat-transporter-mqtt)
|
|
484
|
+
- [Redis](https://github.com/integreat-io/integreat-transporter-redis)
|
|
551
485
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
mappings: {
|
|
555
|
-
entry: {
|
|
556
|
-
attributes: {...},
|
|
557
|
-
qualifier: 'type="entry"'
|
|
558
|
-
},
|
|
559
|
-
admin: {
|
|
560
|
-
attributes: {...},
|
|
561
|
-
qualifier: [
|
|
562
|
-
'type="account"',
|
|
563
|
-
'permissions.roles[]="admin"'
|
|
564
|
-
]
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
```
|
|
486
|
+
You may write your own transporters if your case is not covered by any of these.
|
|
487
|
+
Documentation on developing transporters are coming.
|
|
568
488
|
|
|
569
|
-
|
|
570
|
-
|
|
489
|
+
Integreat will handle the transporters based on you configurations, but there
|
|
490
|
+
are some specifics to each transporter, like HTTP needing an `uri` option or
|
|
491
|
+
MongoDb needing a `collection` option. See the documentation of each transporter
|
|
492
|
+
for more.
|
|
571
493
|
|
|
572
|
-
|
|
494
|
+
## Adapters
|
|
573
495
|
|
|
574
|
-
|
|
575
|
-
|
|
496
|
+
Adapters are working together with transporters to prepare the incoming and
|
|
497
|
+
outgoing data in accordance with the type of services they support.
|
|
498
|
+
|
|
499
|
+
As an example, the HTTP transporter will return data from a response as a
|
|
500
|
+
string, since there is no common way to treat the response body. So for a JSON
|
|
501
|
+
API, you will configure the JSON adapter to make sure the data from the
|
|
502
|
+
mutations are sent as a JSON string, and that the JSON comming back from the
|
|
503
|
+
service is parsed before mutation starts. For a service using XML, you would
|
|
504
|
+
instead set up the XML adapter, and perhaps also the SOAP adapter, to again
|
|
505
|
+
stringify and parse the data going back and forth.
|
|
506
|
+
|
|
507
|
+
The MongoDb transporter, on the other hand, does not require any adapters, as
|
|
508
|
+
documents from the database will always come as arrays and object, and may be
|
|
509
|
+
fed directly into the mutation pipelines.
|
|
510
|
+
|
|
511
|
+
Integreat currently have the following adapters:
|
|
512
|
+
|
|
513
|
+
- [CSV](https://github.com/integreat-io/integreat-adapter-csv)
|
|
514
|
+
- [JSON](https://github.com/integreat-io/integreat-adapter-json)
|
|
515
|
+
- [SOAP](https://github.com/integreat-io/integreat-adapter-soap)
|
|
516
|
+
- [Url encoded form data](https://github.com/integreat-io/integreat-adapter-form)
|
|
517
|
+
- [XML](https://github.com/integreat-io/integreat-adapter-xml)
|
|
518
|
+
|
|
519
|
+
You may write your own adapters as well, and documentation on this is coming.
|
|
520
|
+
|
|
521
|
+
## Mutations
|
|
522
|
+
|
|
523
|
+
Both on the service and on endpoints, you define mutation pipelines. The service
|
|
524
|
+
mutation is run before the endpoint mutation for data coming from a service, and
|
|
525
|
+
in the oposite order when going to a service.
|
|
526
|
+
|
|
527
|
+
A nice, but sometimes complicated, thing about mutations, is that they are run
|
|
528
|
+
in both directions. They are by default defined for mutating data coming _from_
|
|
529
|
+
a service, and will be run in reverse for data going _to_ a service. In some
|
|
530
|
+
cases this reversing of the pipeline will work as expected without modifications
|
|
531
|
+
-- you define the mutation pipeline for data coming _from_ the service, and the
|
|
532
|
+
reversed pipeline works _to_ as well. But many times you need to make
|
|
533
|
+
adjustments and sometimes you'll have to have separate steps based on the
|
|
534
|
+
direction. We'll get into more details in the following.
|
|
535
|
+
|
|
536
|
+
A mutation pipeline consists of one or more steps that the data will go through,
|
|
537
|
+
before coming out on the other in the desired shape. It helps picturing this as
|
|
538
|
+
an actual pipeline. After each step, data will be in a different shape, and this
|
|
539
|
+
is the input to the next step.
|
|
540
|
+
|
|
541
|
+
You define a pipeline in Integreat with an array, although for a pipeline with
|
|
542
|
+
only one step, you may skip the array for simplicity.
|
|
543
|
+
|
|
544
|
+
Each step may be one of the following:
|
|
545
|
+
|
|
546
|
+
- [**A dot notation path**](#dot-notation-paths), e.g. `path.to.data`. The data
|
|
547
|
+
at that path will be extracted, and will be provided as the data to the next
|
|
548
|
+
step in the pipeline. When going in reverse, the data will be set on that path
|
|
549
|
+
instead.
|
|
550
|
+
- **A mutation object** is an object that basically describes the object you
|
|
551
|
+
want as a result, where the keys are dot notation paths and the values are
|
|
552
|
+
mutation pipelines. Each pipeline on the mutation object will be run on the
|
|
553
|
+
data, and then set on the path, resulting in an object that will be passed on
|
|
554
|
+
to the next step.
|
|
555
|
+
- **A transform object** letting you run a transformer function on the data,
|
|
556
|
+
e.g. `{ $transform: 'number' }` to transform the value into a number, or
|
|
557
|
+
`undefined` if not possible.
|
|
558
|
+
- **A filter object** that will run a transformer function on the data and
|
|
559
|
+
filter away any items not resulting in a truthy value. As an example,
|
|
560
|
+
`{ $filter: 'boolean' }` will filter away anything that is not convertable to
|
|
561
|
+
`true` in JS rules. When applied to an array, you'll get an array where items
|
|
562
|
+
are filtered away. For an object or a plain value, filtering away will means
|
|
563
|
+
`undefined` is passed on to the next step in the pipeline.
|
|
564
|
+
- **An if object** that runs a `then` pipeline if the provided pipeline returns
|
|
565
|
+
truthy, and the `else` pipeline if it returns falsy.
|
|
566
|
+
- **A cast object**, e.g. `{ $cast: 'author' }` that casts the data into a
|
|
567
|
+
schema, removing all properties that is not part of the shape of the schema,
|
|
568
|
+
and transforming all values to the expected types or `undefined` if not
|
|
569
|
+
possible.
|
|
570
|
+
|
|
571
|
+
### Dot notation paths
|
|
572
|
+
|
|
573
|
+
At its most basic, a dot notation path is just a property key, like `content`.
|
|
574
|
+
You may dive into a data structure by adding a key from the next level,
|
|
575
|
+
separated by a dot, like `content.articles`. With an object like this:
|
|
576
576
|
|
|
577
|
-
```
|
|
577
|
+
```javascript
|
|
578
578
|
{
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
<metadataKey>: {
|
|
583
|
-
type: <string>
|
|
584
|
-
}
|
|
579
|
+
content: {
|
|
580
|
+
articles: [{ id: '1' }, { id: '2' }],
|
|
581
|
+
authors: [{ id: 'john' }]
|
|
585
582
|
}
|
|
586
583
|
}
|
|
587
584
|
```
|
|
588
585
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
handling these metadata will be the same, but it is possible to let a service
|
|
592
|
-
handle other services' metadata. If you're getting data from a read-only service,
|
|
593
|
-
but need to, for instance, set the `lastSyncedAt` metadata for this store,
|
|
594
|
-
you'll set up a service as a store for this (the store may also hold other types
|
|
595
|
-
of data). Then the read-only store will be defined with `meta='meta'`, and the
|
|
596
|
-
`meta` schema will have `service='store'`.
|
|
597
|
-
|
|
598
|
-
It will usually make no sense to specify default values for metadata.
|
|
586
|
+
... the path `content.articles` will give you the array
|
|
587
|
+
`[{ id: '1' }, { id: '2' }]`.
|
|
599
588
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
`
|
|
603
|
-
these endpoints will depend on your service.
|
|
589
|
+
You may add brackets to the path to traverse into arrays, e.g.
|
|
590
|
+
`content.articles[0]` will give you the object `{ id: '1' }`, and
|
|
591
|
+
`content.articles[0].id` will give you `'1'`.
|
|
604
592
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
593
|
+
Empty brackets, like `content.articles[]` will ensure that you get an array
|
|
594
|
+
back. If the data at the path is an array, this will return the same as
|
|
595
|
+
`content.articles`, but if the path returns an object or a plain value, it will
|
|
596
|
+
be returned in an array.
|
|
608
597
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
type: <meta type>,
|
|
613
|
-
createdAt: <date>,
|
|
614
|
-
updatedAt: <date>,
|
|
615
|
-
attributes: {
|
|
616
|
-
<key>: <value>
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
```
|
|
598
|
+
When mapping data _to_ a service, the paths are used to reconstruct the data
|
|
599
|
+
format the service expects. Only properties included in the paths will be
|
|
600
|
+
created.
|
|
620
601
|
|
|
621
|
-
|
|
622
|
-
|
|
602
|
+
Arrays are reconstructed with any object or value at the first index, unless a
|
|
603
|
+
single, non-negative index is specified in the path.
|
|
623
604
|
|
|
624
|
-
|
|
605
|
+
You may use a carret `^` to go one level up -- to the parent -- in the data
|
|
606
|
+
(after going down), so after `content.articles`, the path `^.authors` will
|
|
607
|
+
return `[{ id: 'john' }]`. Arrays count as one level, so after
|
|
608
|
+
`content.articles[0]` you will need to go up twice like so: `^.^.authors`.
|
|
625
609
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
properties to describe the ident's permissions, or to make it possible to map
|
|
629
|
-
identities in other systems, to an Integreat ident.
|
|
610
|
+
A double carret `^^` takes you to the top -- the root -- so after
|
|
611
|
+
`content.articles[0].id`, `^^.content.authors` returns `[{ id: 'john' }]`.
|
|
630
612
|
|
|
631
|
-
|
|
613
|
+
Carret notations -- parents and roots -- does not currently work in reverse, but
|
|
614
|
+
they might in a future version.
|
|
632
615
|
|
|
633
|
-
|
|
634
|
-
{
|
|
635
|
-
id: 'ident1',
|
|
636
|
-
tokens: ['facebook|12345', 'twitter|23456'],
|
|
637
|
-
roles: ['admin']
|
|
638
|
-
}
|
|
639
|
-
```
|
|
616
|
+
### Jobs
|
|
640
617
|
|
|
641
|
-
|
|
642
|
-
string with A-Z, a-z, 0-9, \_, and -, and it's unique within one Integreat
|
|
643
|
-
configuration. This means that mapped value from services may be used as ident
|
|
644
|
-
ids, but be careful to set this up right.
|
|
618
|
+
> Editor's note: Write this.
|
|
645
619
|
|
|
646
|
-
|
|
647
|
-
Twitter OAuth to identify it's users, may provide the `'twitter|23456'` token in
|
|
648
|
-
the example above, which will be replaced with this ident when it enters
|
|
649
|
-
Integreat.
|
|
620
|
+
## Schemas
|
|
650
621
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
be
|
|
622
|
+
A central idea to Integreat, is that any integration has two sides; the getting
|
|
623
|
+
of data from one service and the sending of data to another. Instead of setting
|
|
624
|
+
up an integration directly from A to B, you have a schema in middle, and
|
|
625
|
+
configure how data from A will be mutated to a schema, and then have data in
|
|
626
|
+
that schema will be mutated and sent to B.
|
|
656
627
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
and execute actions that the ident have permissions for.
|
|
628
|
+
This is a useful abstraction, and if you ever need to change one side, you can
|
|
629
|
+
do so without involving the other side. If you need to switch out service B with
|
|
630
|
+
service C, you can do so without involving the configuration of service A, or
|
|
631
|
+
you can send data to both B and C, using the same setup for service A.
|
|
662
632
|
|
|
663
|
-
|
|
633
|
+
To be clear, you can setup flows without schemas in Integreat, but at the loss
|
|
634
|
+
of this flexibility and maintainability.
|
|
664
635
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
start with these verbs.
|
|
636
|
+
A schema describe the data you expected to get out of Integreat, or send through
|
|
637
|
+
it. You basically define the fields and their types, and may then cast data to
|
|
638
|
+
that shape. Note that data on an action for a specified type, will be
|
|
639
|
+
automatically cast to that type.
|
|
670
640
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
641
|
+
```javascript
|
|
642
|
+
{
|
|
643
|
+
id: <schema id>,
|
|
644
|
+
plural: <the id in plural>,
|
|
645
|
+
service: <the default service for this schema>,
|
|
646
|
+
shape: {
|
|
647
|
+
<fieldId>: <field type>,
|
|
648
|
+
<fieldId>: {
|
|
649
|
+
$type: <field type>,
|
|
650
|
+
default: <default value>
|
|
651
|
+
const: <value that will override any other value>
|
|
652
|
+
},
|
|
653
|
+
},
|
|
654
|
+
access: <access def>
|
|
655
|
+
}
|
|
656
|
+
```
|
|
675
657
|
|
|
676
|
-
|
|
677
|
-
the
|
|
658
|
+
- `id`: The id of the schema, used to reference it in actions (the payload
|
|
659
|
+
`type`), when casting to the schema with `{ $type: '<schema id>' }`, and to
|
|
660
|
+
signal what schema a data object is cast to (the `$type` prop on typed data
|
|
661
|
+
items). The convention is to use singular mode for the `id`, e.g. if your
|
|
662
|
+
defining a schema for articles, you would give it the id `'article'`.
|
|
663
|
+
- `plural`: When the plural of `id` is not simply a matter of adding an `'s'`,
|
|
664
|
+
you may specify the plural mode here. E.g. `id: 'entry'` would have
|
|
665
|
+
`plural: 'entries'`. This is not used by Integreat right now, but it may be
|
|
666
|
+
used in the future for error messages, generating APIs from schemas, etc.
|
|
667
|
+
- `service`: You may specify a default service for the schema when it makes
|
|
668
|
+
sense. This allows you to dispatch an action for a type without specifying the
|
|
669
|
+
target service, e.g. `{ type: 'GET', payload: { type: 'article' } }`, and have
|
|
670
|
+
Integreat use the default service. This is a way of hiding configuration
|
|
671
|
+
details from the code dispatching the actions, and you may also change the
|
|
672
|
+
default service without changing the dispatching code if need be. You may
|
|
673
|
+
always override this by specifying a `service` on the action payload.
|
|
674
|
+
- `shape`: This is where you define all the fields, see
|
|
675
|
+
[the section below](#the-shape-of-a-schema).
|
|
676
|
+
- `generateId`: Set this to `true` to generate a unique id for the `id` field
|
|
677
|
+
when the data being cast does not provide an `id`. Default is `false`, which
|
|
678
|
+
will just set `id: null`
|
|
679
|
+
- `access`: Integreat lets you define authorization schemes per schema. All use
|
|
680
|
+
of data cast to a schema will then be controlled by the rules you set here.
|
|
681
|
+
See [Access rules](#access-rules) below for details on these rules. Note that
|
|
682
|
+
`access` is optional, but when you get data from a service where any form of
|
|
683
|
+
authentication is used to access the data, you will not be able to do anything
|
|
684
|
+
with the data unless you cast it to a schema with `access` set up (or
|
|
685
|
+
specifically says that you allow raw data from that endpoint).
|
|
686
|
+
|
|
687
|
+
### The shape of a schema
|
|
688
|
+
|
|
689
|
+
The shape is defined by an object where each key is the id of a field, which may
|
|
690
|
+
contain only alphanumeric characters, and may not start with a digit. A schema
|
|
691
|
+
cannot have the same id as a primitive type (see list below).
|
|
692
|
+
|
|
693
|
+
The values on this object define the types of the fields and a few other
|
|
694
|
+
optional features:
|
|
678
695
|
|
|
679
696
|
```javascript
|
|
680
697
|
{
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
SET: {role: 'admin'}
|
|
685
|
-
}
|
|
698
|
+
$type: <field type>,
|
|
699
|
+
default: <default value>
|
|
700
|
+
const: <value that will override any other value>
|
|
686
701
|
}
|
|
687
702
|
```
|
|
688
703
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
704
|
+
The `$type` prop sets the type of the field. The available primitive types, are
|
|
705
|
+
`string`, `integer`, `float` (or `number`), `boolean`, and `date`. A field may
|
|
706
|
+
also have another schema as its type, in which case the id of the schema is set
|
|
707
|
+
in `$type`. An example can be an
|
|
708
|
+
`article` schema with an `author` field of type `user`, referring to a schema
|
|
709
|
+
with id `user`. When casting the `article`, data on the `author` prop will be
|
|
710
|
+
cast with the `user` schema.
|
|
711
|
+
|
|
712
|
+
The `default` value will be used when the field is `undefined`, `null`, or not
|
|
713
|
+
preset in data object being cast to this schema. If `default` is set to a
|
|
714
|
+
function, the function will be run with no argument, and the returned value is
|
|
715
|
+
used as the default value. When no `default` is given, `undefined` is used.
|
|
716
|
+
|
|
717
|
+
The `const` value override any value you provide to the field. It may be useful
|
|
718
|
+
if you want a field to always have a fixed value. Just as for `default`, you may
|
|
719
|
+
set it to a function, in which case the function will be run without arguments
|
|
720
|
+
and the returned value will be used.
|
|
692
721
|
|
|
693
|
-
|
|
722
|
+
If both `const` and `default` are set, `const` will be used.
|
|
694
723
|
|
|
695
|
-
|
|
696
|
-
|
|
724
|
+
When only setting the field type, you don't need to provide the entire object,
|
|
725
|
+
you can just provide the type string.
|
|
697
726
|
|
|
698
|
-
|
|
699
|
-
achieve what we aimed for above, could be:
|
|
727
|
+
Example schema:
|
|
700
728
|
|
|
701
729
|
```javascript
|
|
702
730
|
{
|
|
703
|
-
id: '
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
731
|
+
id: 'article',
|
|
732
|
+
shape: {
|
|
733
|
+
id: 'string', // Not needed, as it is always provided, but it's good to include for clarity
|
|
734
|
+
title: { $type: 'string', default: 'Unnamed article' },
|
|
735
|
+
text: 'string',
|
|
736
|
+
readCount: 'integer',
|
|
737
|
+
archived: { $type: 'boolean', default: false },
|
|
738
|
+
rating: 'float',
|
|
739
|
+
createdAt: 'date',
|
|
740
|
+
updatedAt: 'date'
|
|
741
|
+
},
|
|
742
|
+
access: 'all'
|
|
708
743
|
}
|
|
709
744
|
```
|
|
710
745
|
|
|
711
|
-
|
|
712
|
-
|
|
746
|
+
Note that if you provide the `id` field, it should be set to type `'string'` or
|
|
747
|
+
Integreat will throw. The same happens if you set `createdAt` or `updatedAt` to
|
|
748
|
+
anything else than the type `'date'`. If you don't include these fields,
|
|
749
|
+
Integreat will include the `id` for you, but not `createdAt` or `updatedAt`.
|
|
713
750
|
|
|
714
|
-
|
|
751
|
+
### Typed data
|
|
715
752
|
|
|
716
|
-
|
|
717
|
-
(array is not implemented).
|
|
718
|
-
- `ident` - Authorize only idents with this precise id. May be an array (array
|
|
719
|
-
is not implemented).
|
|
720
|
-
- `roleFromField` - Specify the field name (attribute or relationship) on
|
|
721
|
-
the schema, that will hold the role value. When authorizing a data item with
|
|
722
|
-
an ident, the field value on the item must match a role on the ident.
|
|
723
|
-
- `identFromField` - The same as `roleFromField`, but for an ident id.
|
|
724
|
-
- `allow` - Set to `all`, `auth`, or `none`, to give access to everybody, only
|
|
725
|
-
the authenticated, or no one at all. This is also available in short form –
|
|
726
|
-
use this string instead of a access rule object.
|
|
727
|
-
|
|
728
|
-
Another example, intended for authorizing only the ident matching an account:
|
|
753
|
+
When data is cast to a schema, the data will be in the following format:
|
|
729
754
|
|
|
730
|
-
```
|
|
755
|
+
```
|
|
731
756
|
{
|
|
732
|
-
id:
|
|
733
|
-
|
|
757
|
+
id: <string>,
|
|
758
|
+
$type: <schema>,
|
|
759
|
+
createdAt: <date>,
|
|
760
|
+
updatedAt: <date>,
|
|
761
|
+
<key>: <value>,
|
|
762
|
+
<key>: { id: <string>, $ref: <schema> },
|
|
763
|
+
<key: [{ id: <string>, $type: <schema>, ... }],
|
|
764
|
+
...
|
|
734
765
|
}
|
|
735
766
|
```
|
|
736
767
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
768
|
+
- `id`: The id is mandatory and created by Integreat when it is not included in
|
|
769
|
+
the schema. If you don't map anything to the id prop, it will be set to
|
|
770
|
+
`null`, unless the schema is set up with `generateId: true`, in which case a
|
|
771
|
+
universally unique id will be generated for you.
|
|
772
|
+
- `$type`: Set to the id of the schema by Integreat. This is a signal that the
|
|
773
|
+
data has been cast.
|
|
774
|
+
- `createdAt`: This is not mandatory, but has special meaning. When a schema has
|
|
775
|
+
a `createdAt` field, but the date is not set in the data, it will be set to
|
|
776
|
+
the same as `updatedAt` (if provided) or to the current date/time.
|
|
777
|
+
- `updatedAt`: Just as `createdAt`, this is not mandatory. When a schema has
|
|
778
|
+
an `updatedAt` field, and the date is not set in the data, it will be set to
|
|
779
|
+
the same as `createdAt` (if provided) or the current date/time.
|
|
780
|
+
- `<key>`: Then follows the values of all the fields specified in the schema.
|
|
781
|
+
Any value not provided in the data will be set to their default value or
|
|
782
|
+
`undefined`. Fields with other schemas as their type, will be an object. If
|
|
783
|
+
only the id is provided in the data, the `{ id: <string>, $ref: <schema id> }`
|
|
784
|
+
format will be used, with `$ref` being the id of the field type schema. When
|
|
785
|
+
more data is provided, Integreat will cast it to the target schema and provide
|
|
786
|
+
the entire data object, or array of objects, with the relevant `$type`.
|
|
740
787
|
|
|
741
|
-
###
|
|
788
|
+
### Access rules
|
|
742
789
|
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
relevant service provided the same ident id, and authorization where done on the
|
|
746
|
-
ident id only.)
|
|
790
|
+
Set the `access` property on a schema to enforce permission checking. This
|
|
791
|
+
applies to any service that provides data in this schema.
|
|
747
792
|
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
793
|
+
The simplest access rule is `auth`, which means that anyone can do anything with
|
|
794
|
+
the data of this schema, as long as they are authenticated. Being authenticated
|
|
795
|
+
in this context, means that the dispatched action has an `ident` in the `meta`
|
|
796
|
+
object. See [the section on idents](#idents) for more on this.
|
|
751
797
|
|
|
752
|
-
|
|
753
|
-
match the different props on an ident:
|
|
798
|
+
Example of a schema with an access rule:
|
|
754
799
|
|
|
755
800
|
```javascript
|
|
756
801
|
{
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
roles: 'groups',
|
|
763
|
-
tokens: 'tokens'
|
|
764
|
-
}
|
|
765
|
-
}
|
|
802
|
+
id: 'article',
|
|
803
|
+
shape: {
|
|
804
|
+
// ...
|
|
805
|
+
},
|
|
806
|
+
access: 'auth'
|
|
766
807
|
}
|
|
767
808
|
```
|
|
768
809
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
810
|
+
To signal that the schema really has no need for authorization, use `all`.
|
|
811
|
+
This is not the same as not setting the `auth` prop, as `all` will override
|
|
812
|
+
Integreat's principle of not letting authorized data out of Integreat without
|
|
813
|
+
an access rule. `all` allows anybody to access the data, even the
|
|
814
|
+
unauthenticated.
|
|
815
|
+
|
|
816
|
+
On the other end of the spectrum, `none` will allow no one to access data cast
|
|
817
|
+
to this schema, no matter who they are.
|
|
818
|
+
|
|
819
|
+
For more fine-grained rules, set `access` to an access definition object with
|
|
820
|
+
rules telling Integreat which rights to require when performing different
|
|
821
|
+
actions with a given schema. These rules apply to the [idents](#idents) set on
|
|
822
|
+
the action `meta` object.
|
|
823
|
+
|
|
824
|
+
The following access rule props are available:
|
|
825
|
+
|
|
826
|
+
- `allow`: Set to `all`, `auth`, or `none`, to give access to everybody, only
|
|
827
|
+
the authenticated, or no one at all. This is what we describe in short form
|
|
828
|
+
above, where we provided this string instead of a access rule object.
|
|
829
|
+
- `role`: Authorize only idents with this `role`. May also be an array.
|
|
830
|
+
- `ident`: Authorize only idents with this precise `id`. May also be an array.
|
|
831
|
+
- `roleFromField`: Same as `role`, except the role is gotten from a field in the
|
|
832
|
+
schema. When authorizing data cast to this schema, the value of the role field
|
|
833
|
+
needs to be identical to (one of) the role(s) of the ident.
|
|
834
|
+
- `identFromField` - The same as `roleFromField`, but for an ident id.
|
|
787
835
|
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
836
|
+
In addition, you may override the general access rules of a schema with specific
|
|
837
|
+
rules for a type of action, by setting an `action` object with access rules for
|
|
838
|
+
action types. Here's an example of an access definition for allowing all
|
|
839
|
+
authorized idents to `GET` data in a certain shema, requiring the role `admin`
|
|
840
|
+
for `SET`s, and disallowing all other actions with the general rule
|
|
841
|
+
`allow: 'none'`:
|
|
791
842
|
|
|
792
843
|
```javascript
|
|
793
844
|
{
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
845
|
+
id: 'article',
|
|
846
|
+
shape: {
|
|
847
|
+
// ...
|
|
848
|
+
},
|
|
849
|
+
access: {
|
|
850
|
+
allow: 'none',
|
|
851
|
+
actions: {
|
|
852
|
+
GET: { allow: 'auth' },
|
|
853
|
+
SET: { role: 'admin' }
|
|
854
|
+
}
|
|
798
855
|
}
|
|
799
856
|
}
|
|
800
857
|
```
|
|
801
858
|
|
|
802
|
-
|
|
803
|
-
|
|
859
|
+
Note that these action specific rules only applies to actions being sent to a
|
|
860
|
+
service. Some actions will never reach a service, but will instead trigger other
|
|
861
|
+
actions, and access will be granted or rejected only for the actions that are
|
|
862
|
+
about to be sent to a service. E.g. when you dispatch a `SYNC` action, it starts
|
|
863
|
+
off by dispatching one or more `GET` actions. The `SYNC` action is not subjected
|
|
864
|
+
to any access rules, but the `GET` actions are, and so the `SYNC` will fail if
|
|
865
|
+
one of the `GET` is rejected.
|
|
804
866
|
|
|
805
|
-
|
|
806
|
-
with the `completeIdent` middleware:
|
|
867
|
+
Another example, intended for authorizing only the ident matching a user:
|
|
807
868
|
|
|
808
869
|
```javascript
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
870
|
+
{
|
|
871
|
+
id: 'user',
|
|
872
|
+
shape: {
|
|
873
|
+
// ...
|
|
874
|
+
},
|
|
875
|
+
access: { identFromField: 'id' }
|
|
876
|
+
}
|
|
812
877
|
```
|
|
813
878
|
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
ident with the specified token. If no ident is found, the original ident is
|
|
818
|
-
kept.
|
|
879
|
+
Here, only actions where the ident id is the same as the id of the user data,
|
|
880
|
+
will be allowed. This means that authenticated users (idents) may only
|
|
881
|
+
only access their own user data.
|
|
819
882
|
|
|
820
883
|
## Actions
|
|
821
884
|
|
|
822
|
-
Actions are serializable objects that are dispatched to Integreat
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
885
|
+
Actions are serializable objects that are dispatched to Integreat. It is a
|
|
886
|
+
important that they are serializable, as this allows them to, for instance, be
|
|
887
|
+
put in a database persisted queue and be picked up of another Intergreat
|
|
888
|
+
instance in another process. Note that `Date` objects are considered
|
|
889
|
+
serializable, as they are converted to ISO date strings when needed.
|
|
826
890
|
|
|
827
891
|
An action looks like this:
|
|
828
892
|
|
|
829
|
-
```
|
|
893
|
+
```javascript
|
|
830
894
|
{
|
|
831
|
-
type: <
|
|
832
|
-
payload: <payload>,
|
|
833
|
-
meta: <meta
|
|
895
|
+
type: <action type>,
|
|
896
|
+
payload: <payload object>,
|
|
897
|
+
meta: <meta object>
|
|
834
898
|
}
|
|
835
899
|
```
|
|
836
900
|
|
|
837
|
-
`type
|
|
838
|
-
Integreat
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
901
|
+
- `type`: This is the id of one [of the action handlers](#available-action-handlers)
|
|
902
|
+
that comes with Integreat, e.g. `GET`. When you dispatch an action, it is
|
|
903
|
+
handed off to this handler (after some inital preperation). You may write
|
|
904
|
+
your own action handlers as well.
|
|
905
|
+
- `payload`: Holds parameters and [data](#typed-data) for this action. There are
|
|
906
|
+
some reserved [payload properties](#payload-properties), and the rest will be
|
|
907
|
+
made available to you in the mutation pipeline.
|
|
908
|
+
- `meta`: Holds information about the action that does not belong in the
|
|
909
|
+
payload, like the ident of the user dispatching, action id, etc. There are
|
|
910
|
+
some reserved [meta properties](#meta-properties), but you may add your own
|
|
911
|
+
here too.
|
|
912
|
+
|
|
913
|
+
When an action is dispatched, it returns a [response object](#action-response)
|
|
914
|
+
with status, data, error message, etc.
|
|
915
|
+
|
|
916
|
+
Note that in a mutation pipeline, action handler, or middleware, the
|
|
917
|
+
response object is provided as a fourth property on the action. You will most
|
|
918
|
+
likely meet this at least when setting up mutations.
|
|
919
|
+
|
|
920
|
+
### Payload properties
|
|
921
|
+
|
|
922
|
+
The payload is, together with the action `type`, a description to Integreat and
|
|
923
|
+
the service of what to do. A design principle of Integreat has been to have as
|
|
924
|
+
little specifics in these payload, so actions may be discpatched to service
|
|
925
|
+
without knowing how the service works. This is not always possible, at least not
|
|
926
|
+
yet, but it's a good principle to follow, also when you configure services and
|
|
927
|
+
plan what props need to be sent in the action payload.
|
|
928
|
+
|
|
929
|
+
You may set any properties on the payload, and they will be be available to you
|
|
930
|
+
in the service endpoint match and in the service mutations. Some properties have
|
|
931
|
+
special meanings, though, and you should avoid using them for anything else:
|
|
932
|
+
|
|
933
|
+
- `type`: The type of the data the action sends and/or receives. This refers to
|
|
934
|
+
the `id` of a schema, and you will usually want to set this. Data provided
|
|
935
|
+
in the payload `data` and response `data` will be cast to this schema. If
|
|
936
|
+
you're dealing with several types in one action, you may set an array of
|
|
937
|
+
types, but will have to cast the data in the mutation yourself. Integreat will
|
|
938
|
+
validate that the data you send and receive is indeed of that type, and will
|
|
939
|
+
give you an auth error if not. (See
|
|
940
|
+
[`allowRawRequest` and `allowRawResponse` on endpoints](#endpoints) for an
|
|
941
|
+
exception.)
|
|
942
|
+
- `id`: You provide an id when you want to address a specific data item, usually
|
|
943
|
+
when you want to fetch one data item with an action like
|
|
944
|
+
`{ type: 'GET', payload: { type: 'article', id: '12345' } }`. You may also
|
|
945
|
+
supply an array of ids to fetch several data items by id. When setting data,
|
|
946
|
+
the id will instead be specified in the `data` when appropriate.
|
|
947
|
+
- `data`: The data to send to a service. This may be any data that makes sense
|
|
948
|
+
to the service, but will usually be a [typed data object](#typed-data) or an
|
|
949
|
+
array of typed data objects, where the adjustments for the service happens in
|
|
950
|
+
service mutations.
|
|
951
|
+
- `service`: The `id` of the service to send this action to. If not specified,
|
|
952
|
+
Integreat will try and find the right service from the `type`.
|
|
953
|
+
- `targetService`: An alias of `service`.
|
|
954
|
+
- `sourceService`: When data comes from a different service and has not been
|
|
955
|
+
mutated and cast yet, the `sourceService` property will tell Integreat to run
|
|
956
|
+
the data through the source service configuration before passing the action on
|
|
957
|
+
to an action handler. An example may be data coming in through an API, where
|
|
958
|
+
the API is configured as a service in Integreat. Note that this property is
|
|
959
|
+
usually set by transporters in their `listen()` methods, but you may also set
|
|
960
|
+
it directly on the action when it makes sense.
|
|
961
|
+
- `endpoint`: Set this to the `id` of a service endpoint when you want to
|
|
962
|
+
override the endpoint match rules of Integreat. This should only be used when
|
|
963
|
+
it is really necessary. Normally, you should instead design the match
|
|
964
|
+
properties to match the correct actions.
|
|
965
|
+
|
|
966
|
+
For services that support pagination, i.e. fetching data in several rounds, one
|
|
967
|
+
page at a time, the following properties may be supported:
|
|
968
|
+
|
|
969
|
+
- `pageSize`: The number of data items to fetch in one request to the service.
|
|
970
|
+
By specifying a page size, you signal that you would like to use pagination,
|
|
971
|
+
and without it all other pagination properties will be disregarded. You will
|
|
972
|
+
get the number of data items you specify (or less, if there are not that many
|
|
973
|
+
items), and may then go on to dispatch an action for the next page. See
|
|
974
|
+
[pagination](#pagination) for more
|
|
975
|
+
- `pageOffset`: The number of data items to "skip" before returning the number
|
|
976
|
+
of items specified in `pageSize`. If you ask for 500 items, the first action
|
|
977
|
+
should have `pageOffset: 0` (or not specified), the next action
|
|
978
|
+
`pageOffset: 500`, then `pageOffset: 1000`, and so on.
|
|
979
|
+
- `page`: The index of the page to fetch. Unlike most other indexes, this starts
|
|
980
|
+
with `1` being the first page. The effect is the same as `pageOffset`, it's
|
|
981
|
+
just a different way of specifying it. `page: 1` is the same as
|
|
982
|
+
`pageOffset: 0`, and `page: 2` is the same as `pageOffset: 500`, given a
|
|
983
|
+
`pageSize: 500`. Integreat will actually calculate both before sending it to
|
|
984
|
+
the transporter, as different types of services support different types of
|
|
985
|
+
pagination.
|
|
986
|
+
- `pageAfter`: As an alternative to specifying the number of items to skip, you
|
|
987
|
+
may ask for the items after the item with the id you provide as `pageAfter`.
|
|
988
|
+
If the last item of the first page is `'12345'`, you may set
|
|
989
|
+
`pageAfter: '12345'` to get the next page.
|
|
990
|
+
- `pageBefore`: This works the same as `pageAfter`, except it is intended for
|
|
991
|
+
when your going backward and fetching a number items _before_ the id you
|
|
992
|
+
provide.
|
|
993
|
+
- `pageId`: Some services and/or transporters will return an id for the next
|
|
994
|
+
page, as an alternative to the other properties mentioned above. You then
|
|
995
|
+
apply this id as `pageId` when dispatching the action for the next page. Note
|
|
996
|
+
that this id may hold internal logic from the transporter, but you should
|
|
997
|
+
never rely on this logic and simply use it as an id.
|
|
998
|
+
|
|
999
|
+
> **Important note:** Pagination has to be supported by the service and your
|
|
1000
|
+
> service configuration, and sometimes also the transporter. Integreat prepares
|
|
1001
|
+
> and passes on these pagination properties, but if the service disregards them,
|
|
1002
|
+
> there is little Integreat can do – except limiting the number of items
|
|
1003
|
+
> returned. It's up to you to figure out how to configure pagination for a
|
|
1004
|
+
> service, but youshould use these pagination properties to support it, to make
|
|
1005
|
+
> this predictable. It also lets you use actions such as `GET_ALL`, that support
|
|
1006
|
+
> pagination.
|
|
1007
|
+
|
|
1008
|
+
Finally, there are some properties that has no special meaning to Integreat
|
|
1009
|
+
itself, but that may be set on incoming actions from transporters. These should
|
|
1010
|
+
ideally be used in the same way or avoided:
|
|
1011
|
+
|
|
1012
|
+
- `contentType`: A keyword for the type of content in the `data` property. E.g.
|
|
1013
|
+
`application/json` or `text/plain`.
|
|
1014
|
+
- `headers`: An object of header information, given as key/value pairs. The
|
|
1015
|
+
value may be a string or an array of strings. This may be HTTP headers or any
|
|
1016
|
+
other type of header information that makes sense to a service.
|
|
1017
|
+
- `hostname`: The host name that incoming request was sent to. For HTTP, this
|
|
1018
|
+
will be the domain name the request was sent to.
|
|
1019
|
+
- `method`: The method of the incoming request. The HTTP transporter will set
|
|
1020
|
+
this to `GET`, `POST`, `PUT`, etc. from the incoming request.
|
|
1021
|
+
- `path`: The path from the incoming request. For the HTTP transporter, this
|
|
1022
|
+
will be the part of the url after the domain name, like
|
|
1023
|
+
`'/v1.0/articles/12345'`.
|
|
1024
|
+
- `port`: The port number of the incoming request.
|
|
1025
|
+
- `queryParams`: An object of query params from the incoming request, usually
|
|
1026
|
+
key/value pairs where the value is a string or an array of strings. For HTTP,
|
|
1027
|
+
this will be the part after the question mark.
|
|
1028
|
+
|
|
1029
|
+
### Meta properties
|
|
1030
|
+
|
|
1031
|
+
The action meta object is for information about an action that does not directly
|
|
1032
|
+
define the action itself. The difference may be subtle in some cases, but the
|
|
1033
|
+
general rule is a piece of information affects how the action is run, it should
|
|
1034
|
+
be in the payload. E.g. the type of items to fetch is in the payload, while the
|
|
1035
|
+
time the action was dispatched would go in the meta.
|
|
1036
|
+
|
|
1037
|
+
This rule does not always hold, e.g. for information on the user dispatching the
|
|
1038
|
+
action in `ident` on the meta object. Different idents may result in different
|
|
1039
|
+
data being returned from the service, but still the action to perform is the
|
|
1040
|
+
same, so it makes sense to have the ident in the meta object.
|
|
1041
|
+
|
|
1042
|
+
You may set your own meta properties, but in most cases you'll probably rather
|
|
1043
|
+
set payload properties.
|
|
843
1044
|
|
|
844
1045
|
Current meta properties reserved by Integreat:
|
|
845
1046
|
|
|
846
|
-
- `
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
- `
|
|
850
|
-
|
|
1047
|
+
- `ident`: The ident to authorize the action with. May hold an `id`, `roles`,
|
|
1048
|
+
`tokens`, and a few other options. See
|
|
1049
|
+
[the section on idents](#idents-and-security-rules).
|
|
1050
|
+
- `id`: The id of the action itself. You may set this yourself or let Integreat
|
|
1051
|
+
generate a universally unique id for you. Useful for logging and may be used
|
|
1052
|
+
by queues.
|
|
1053
|
+
- `cid`: A correlation id that may be used to group actions that belong
|
|
1054
|
+
together, primarily for logging purposes. You may set this yourself or
|
|
1055
|
+
Integreat will set it to the same as the `id`. Some Integreat action handlers
|
|
1056
|
+
will dispatch sub actions using the `cid` from the original action.
|
|
1057
|
+
- `dispatchedAt`: Timestamp for when the action was dispatched (set by
|
|
1058
|
+
Integreat).
|
|
1059
|
+
- `queue`: Signals to Integreat that an action may be queued. Set to `true` when
|
|
1060
|
+
you want the action to be queued, but executed as soon as possible. Set to a
|
|
1061
|
+
UNIX timestamp (number) to schedule for a later time. If no queue is set up,
|
|
1062
|
+
the action will be dispatched right away. More on this under
|
|
1063
|
+
[the section on queues](#queue).
|
|
1064
|
+
- `queuedAt`: Timestamp for when the action was pushed to the queue (set by
|
|
1065
|
+
Integreat).
|
|
1066
|
+
- `options`: Used for passing the processed service endpoint options object to
|
|
1067
|
+
a transporter. The `options` object is available through mutations, so that
|
|
1068
|
+
you may modify it futher before it goes to the transporter.
|
|
1069
|
+
- `authorized`: An internal flag signaling that the action has been authorized.
|
|
1070
|
+
Will be removed from any dispatched actions.
|
|
1071
|
+
|
|
1072
|
+
### Action response
|
|
1073
|
+
|
|
1074
|
+
When you dispatch an action, you will get a response object back in this format:
|
|
851
1075
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
Retrieving from a service will return an Intgreat response object of the
|
|
855
|
-
following format:
|
|
856
|
-
|
|
857
|
-
```
|
|
1076
|
+
```javascript
|
|
858
1077
|
{
|
|
859
|
-
status: <
|
|
860
|
-
data: <
|
|
861
|
-
error: <
|
|
1078
|
+
status: <status code>,
|
|
1079
|
+
data: <data from the service, usually mutated>,
|
|
1080
|
+
error: <error message>,
|
|
1081
|
+
warning: <warning message>,
|
|
1082
|
+
access: <ident of the user>,
|
|
1083
|
+
paging: <pagination objects>,
|
|
1084
|
+
params: <key/value pairs>,
|
|
1085
|
+
headers: <key/value pairs>,
|
|
1086
|
+
responses: <array of sub-responses when relevant>,
|
|
1087
|
+
meta: <meta object>
|
|
862
1088
|
}
|
|
863
1089
|
```
|
|
864
1090
|
|
|
865
|
-
|
|
1091
|
+
- `status`: The status of the action. Will be `ok` when everything went well,
|
|
1092
|
+
see [list of status codes](#status-codes) below for more.
|
|
1093
|
+
- `data`: Any data returned from the service, after being modified by the
|
|
1094
|
+
mutation pipelines from your service and endpoint configuration. It will be
|
|
1095
|
+
cast to [typed data](#typed-data) through the schema specified by the payload
|
|
1096
|
+
`type`, if it is set to a single type and the endpoint `allowRawResponse` is
|
|
1097
|
+
not set to `true`.
|
|
1098
|
+
- `error`: All error statuses (i.e. not `ok` or `queued`) will return an error
|
|
1099
|
+
message, some may include error messages from the service.
|
|
1100
|
+
- `warning`: When the action was successful, but there still was something you
|
|
1101
|
+
should know, the warning message is where you'll get noticed. An example is
|
|
1102
|
+
when you get an array of data items, but some of them was removed due to the
|
|
1103
|
+
access of the ident on the action.
|
|
1104
|
+
- `paging`: For services and transporters that support
|
|
1105
|
+
[pagination](#pagination), this object will hold information about how to get
|
|
1106
|
+
the next or previous page, in a `next` or `prev` object. These objects are
|
|
1107
|
+
essentially the payloads you need to dispatch (with the same action `type` and
|
|
1108
|
+
meta), to get the next or previous page. If there is no next or previous page,
|
|
1109
|
+
the corresponding prop will not be set on the `paging` object. When pagination
|
|
1110
|
+
is not relevant or used, the `paging` object may be missing completely.
|
|
1111
|
+
- `params`: Integreat never sets this, but you may set it in your mutations to
|
|
1112
|
+
provide parameters from a service that does not belong in the `data`.
|
|
1113
|
+
- `headers`: Integreat never sets this, but you may set it in your mutations to
|
|
1114
|
+
provide header key/value pairs from a service. Typically used when this is a
|
|
1115
|
+
response to an incoming request that support headers, like HTTP do.
|
|
1116
|
+
- `responses`: In some cases, an action will run several sub-actions, like
|
|
1117
|
+
`SYNC` or `RUN`. The action handlers _may_ then provide an array of all the
|
|
1118
|
+
sub-response objects here.
|
|
1119
|
+
|
|
1120
|
+
> Editor's note: Do we return access in responses?
|
|
1121
|
+
> Editor's note: Do we return meta in responses?
|
|
1122
|
+
> Editor's note: Is it correct that queues return the id in the data?
|
|
1123
|
+
|
|
1124
|
+
When the status is `queued`, the id of the queued action may found in
|
|
1125
|
+
`response.data.id`. This is the id assigned by the queue, and not necessarily
|
|
1126
|
+
the same as `action.meta.id`.
|
|
1127
|
+
|
|
1128
|
+
### Status codes
|
|
1129
|
+
|
|
1130
|
+
The `status` or the action response will be one of the following status codes:
|
|
866
1131
|
|
|
867
1132
|
- `ok`: Everything is well, data is returned as expected
|
|
868
|
-
- `queued`: The action has been queued
|
|
869
|
-
- `
|
|
870
|
-
|
|
1133
|
+
- `queued`: The action has been queued. This is regarded as a success status
|
|
1134
|
+
- `noaction`: The action did nothing, e.g. when a `SYNC` action has no data to
|
|
1135
|
+
sync
|
|
1136
|
+
- `notfound`: Tried to get or modify a resource that does not exist
|
|
871
1137
|
- `timeout`: The attempt to perform the action timed out
|
|
872
1138
|
- `autherror`: An authentication request failed
|
|
873
1139
|
- `noaccess`: Authentication is required or the provided auth is not enough
|
|
@@ -875,121 +1141,121 @@ The `status` will be one of the following status codes:
|
|
|
875
1141
|
- `badresponse`: Response data is not as expected
|
|
876
1142
|
- `error`: Any other error
|
|
877
1143
|
|
|
878
|
-
|
|
879
|
-
usually be mapped data in [Integreat's data format](#the-data-format), but
|
|
880
|
-
essentially, the data format depends on which action it comes from.
|
|
1144
|
+
### Idents
|
|
881
1145
|
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
In case of any other status than `ok` or `queued`, there will be no `data`, and
|
|
887
|
-
instead the `error` property will be set to an error message, usually returned
|
|
888
|
-
from the adapter.
|
|
889
|
-
|
|
890
|
-
`data` and `error` will never be set at the same time.
|
|
891
|
-
|
|
892
|
-
The same principles applies when an action is sending data or performing an
|
|
893
|
-
action other than receiving data. On success, the returned `status` will be
|
|
894
|
-
`ok`, and the `data` property will hold whatever the adapter returns. There is
|
|
895
|
-
no guaranty on the returned data format in these cases.
|
|
896
|
-
|
|
897
|
-
### The data format
|
|
1146
|
+
An ident in Integreat is basically an id unique to one participant in the
|
|
1147
|
+
security scheme. It is represented by an object that may also have other
|
|
1148
|
+
properties to describe the ident's permissions, or to make it possible to match
|
|
1149
|
+
to identities in other services.
|
|
898
1150
|
|
|
899
|
-
|
|
1151
|
+
Example ident:
|
|
900
1152
|
|
|
901
|
-
```
|
|
1153
|
+
```javascript
|
|
902
1154
|
{
|
|
903
|
-
id:
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
updatedAt: <date>,
|
|
907
|
-
attributes: {
|
|
908
|
-
<attrKey>: <value>,
|
|
909
|
-
...
|
|
910
|
-
},
|
|
911
|
-
relationships: {
|
|
912
|
-
<relKey>: {id: <string>, type: <schema>},
|
|
913
|
-
<relKey: [{id: <string>, type: <schema>, ...],
|
|
914
|
-
...
|
|
915
|
-
}
|
|
1155
|
+
id: 'ident1',
|
|
1156
|
+
tokens: ['facebook|12345', 'twitter|23456'],
|
|
1157
|
+
roles: ['admin']
|
|
916
1158
|
}
|
|
917
1159
|
```
|
|
918
1160
|
|
|
919
|
-
`id
|
|
920
|
-
Integreat
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1161
|
+
- `id`: A unique string identifying the ident. The actual value is irrelevant to
|
|
1162
|
+
Integreat, as long as it is a string with A-Z, a-z, 0-9, \_, and -, and it's
|
|
1163
|
+
unique within one Integreat configuration. This means that mapped values from
|
|
1164
|
+
services may be used as ident ids, as long as they are unique among these
|
|
1165
|
+
services.
|
|
1166
|
+
- `tokens`: A list of values that may identify this ident in other services. For
|
|
1167
|
+
example, an api that uses Twitter OAuth to identify its users, may provide
|
|
1168
|
+
the `'twitter|23456'` token in the example above, which will be replaced with
|
|
1169
|
+
this ident when it enters Integreat.
|
|
1170
|
+
- `roles`: A list of roles or permissions given to this ident. The roles are
|
|
1171
|
+
custom defined per setup, and may be mapped to roles from other systems. When
|
|
1172
|
+
setting the auth rules for a schema, you specify required rules so that to get
|
|
1173
|
+
data cast in this schema, an ident with e.g. the role `admin` must be
|
|
1174
|
+
provided.
|
|
1175
|
+
|
|
1176
|
+
Actions are authenticated by setting an ident on the `meta.ident` property. It's
|
|
1177
|
+
up to the code dispatching an action to get hold of the properties of an ident
|
|
1178
|
+
in a secure way. Once Integreat receives an ident through a dispatch, it will
|
|
1179
|
+
assume this is accurate information and uphold its part of the security
|
|
1180
|
+
agreement and only return data and execute actions that the ident have
|
|
1181
|
+
permissions for.
|
|
1182
|
+
|
|
1183
|
+
Note that it's possible to set up
|
|
1184
|
+
[the `completeIdent` middleware](#completeIdent-middleware) for combining
|
|
1185
|
+
information from the authenticator with user information held e.g. in a
|
|
1186
|
+
database.
|
|
1187
|
+
|
|
1188
|
+
### Available action handlers
|
|
925
1189
|
|
|
926
1190
|
#### `GET`
|
|
927
1191
|
|
|
928
|
-
Get
|
|
929
|
-
|
|
1192
|
+
Get data from a service. You receive the data on the `data` property, after it
|
|
1193
|
+
has been run through your service and endpoint mutations.
|
|
930
1194
|
|
|
931
|
-
Example GET action:
|
|
1195
|
+
Example GET action to a collection of data items:
|
|
932
1196
|
|
|
933
1197
|
```javascript
|
|
934
1198
|
{
|
|
935
1199
|
type: 'GET',
|
|
936
|
-
payload: {
|
|
937
|
-
type: 'entry'
|
|
938
|
-
}
|
|
1200
|
+
payload: { type: 'article' }
|
|
939
1201
|
}
|
|
940
1202
|
```
|
|
941
1203
|
|
|
942
|
-
In the example above, the service is inferred from the payload `type` property.
|
|
943
|
-
Override this by supplying the id of a service as a `service` property.
|
|
944
|
-
|
|
945
1204
|
By providing an `id` property on `payload`, the item with the given id and type
|
|
946
|
-
is fetched, if it exists
|
|
1205
|
+
is fetched, if it exists:
|
|
947
1206
|
|
|
948
|
-
|
|
949
|
-
|
|
1207
|
+
```javascript
|
|
1208
|
+
{
|
|
1209
|
+
type: 'GET',
|
|
1210
|
+
payload: { type: 'article', id: '12345' }
|
|
1211
|
+
}
|
|
1212
|
+
```
|
|
950
1213
|
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
the service data.
|
|
1214
|
+
See [the section on payload properties](#payload-properties) for more properties
|
|
1215
|
+
that may be used with the `GET` action.
|
|
954
1216
|
|
|
955
|
-
#### `
|
|
1217
|
+
#### `GET_ALL`
|
|
956
1218
|
|
|
957
|
-
|
|
958
|
-
`data` property is an array of normalized objects in the format retrieved from
|
|
959
|
-
the service. The data is not mapped in any way, and the only thing guarantied, is
|
|
960
|
-
that this is a JavaScript object.
|
|
1219
|
+
Will run as many `GET` actions as needed to the get all available pages of data.
|
|
961
1220
|
|
|
962
|
-
|
|
963
|
-
lookup mappings for any given type. The only reason to include a `type` in the
|
|
964
|
-
payload, would be if the endpoint uri requires a `type` parameter.
|
|
1221
|
+
The action ...
|
|
965
1222
|
|
|
966
|
-
|
|
967
|
-
|
|
1223
|
+
```javascript
|
|
1224
|
+
{
|
|
1225
|
+
type: 'GET_ALL',
|
|
1226
|
+
payload: { type: 'article', pageSize: 500 }
|
|
1227
|
+
}
|
|
1228
|
+
```
|
|
968
1229
|
|
|
969
|
-
|
|
1230
|
+
... will dispatch the following action is sequence:
|
|
970
1231
|
|
|
971
1232
|
```javascript
|
|
972
1233
|
{
|
|
973
|
-
type: '
|
|
974
|
-
payload: {
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1234
|
+
type: 'GET',
|
|
1235
|
+
payload: { type: 'article', pageSize: 500 }
|
|
1236
|
+
}
|
|
1237
|
+
```
|
|
1238
|
+
|
|
1239
|
+
```javascript
|
|
1240
|
+
{
|
|
1241
|
+
type: 'GET',
|
|
1242
|
+
payload: { type: 'article', pageSize: 500, pageOffset: 500 }
|
|
978
1243
|
}
|
|
979
1244
|
```
|
|
980
1245
|
|
|
981
|
-
|
|
982
|
-
|
|
1246
|
+
... and so on, until we get no more data.
|
|
1247
|
+
|
|
1248
|
+
See [the section on pagination](#pagination) for more on the paging properties.
|
|
983
1249
|
|
|
984
1250
|
#### `GET_META`
|
|
985
1251
|
|
|
986
|
-
Get metadata for a service.
|
|
987
|
-
|
|
1252
|
+
Get metadata for a service. See
|
|
1253
|
+
[the section on metadata](#configuring-service-metadata) for how to set this up.
|
|
988
1254
|
|
|
989
|
-
The
|
|
990
|
-
|
|
1255
|
+
The `data` of the response from this aciton contains the `service` (the service
|
|
1256
|
+
id) and `meta` object with the metadata set as properties.
|
|
991
1257
|
|
|
992
|
-
Example GET_META action:
|
|
1258
|
+
Example `GET_META` action:
|
|
993
1259
|
|
|
994
1260
|
```javascript
|
|
995
1261
|
{
|
|
@@ -1020,55 +1286,43 @@ If the action has no `keys`, all metadata set on the service will be retrieved.
|
|
|
1020
1286
|
The `keys` property may be an array of keys to retrieve several in one request,
|
|
1021
1287
|
or a single key.
|
|
1022
1288
|
|
|
1023
|
-
Note that the service must be set up to handle metadata. See
|
|
1024
|
-
[Configuring metadata](#configuring-metadata) for more.
|
|
1025
|
-
|
|
1026
1289
|
#### `SET`
|
|
1027
1290
|
|
|
1028
|
-
Send data to a service.
|
|
1029
|
-
|
|
1291
|
+
Send data to a service. The data to send is provided in the payload `data`
|
|
1292
|
+
property. Recomended practice is to provide the data as
|
|
1293
|
+
[typed data](#typed-data), i.e. data objects cast to a schema, and let
|
|
1294
|
+
mutations on the service endpoint modify it to the format the service expects.
|
|
1030
1295
|
|
|
1031
|
-
|
|
1032
|
-
|
|
1296
|
+
Any data coming back from the service, will be provided on `response.data` and
|
|
1297
|
+
may be mutated through service endpoint mutations, just as for [`GET`](#get)
|
|
1298
|
+
actions.
|
|
1033
1299
|
|
|
1034
|
-
Example SET action:
|
|
1300
|
+
Example `SET` action:
|
|
1035
1301
|
|
|
1036
1302
|
```javascript
|
|
1037
1303
|
{
|
|
1038
1304
|
type: 'SET',
|
|
1039
1305
|
payload: {
|
|
1040
|
-
|
|
1306
|
+
type: 'article',
|
|
1041
1307
|
data: [
|
|
1042
|
-
{id: '
|
|
1043
|
-
{id: '
|
|
1308
|
+
{ id: '12345', $type: 'article', title: 'First article' },
|
|
1309
|
+
{ id: '12346', $type: 'article', title: 'Second article' }
|
|
1044
1310
|
]
|
|
1045
1311
|
}
|
|
1046
1312
|
}
|
|
1047
1313
|
```
|
|
1048
1314
|
|
|
1049
|
-
In the example above, the `service` is specified in the payload. Specifying a
|
|
1050
|
-
`type` to infer the service from is also possible, but not recommended, as it
|
|
1051
|
-
may be removed in future versions of Integreat.
|
|
1052
|
-
|
|
1053
|
-
The endpoint will be picked according to the matching properties, unless an
|
|
1054
|
-
endpoint id is supplied as an `endpoint` property of `payload`.
|
|
1055
|
-
|
|
1056
|
-
To send only fields mapped from the action data to the service, set
|
|
1057
|
-
`sendNoDefaults: true` on the endpoint config to cast the data going to the
|
|
1058
|
-
service without using default values. This will not affect the data coming back
|
|
1059
|
-
from the action, but set `returnNoDefaults: true` to leave defaults out of the
|
|
1060
|
-
response data.
|
|
1061
|
-
|
|
1062
1315
|
#### `SET_META`
|
|
1063
1316
|
|
|
1064
|
-
Set metadata on a service.
|
|
1065
|
-
|
|
1066
|
-
|
|
1317
|
+
Set metadata on a service. The payload should contain the `service` to get
|
|
1318
|
+
metadata for (the service id), and a `meta` object, with all metadata to set as
|
|
1319
|
+
properties.
|
|
1067
1320
|
|
|
1068
|
-
|
|
1069
|
-
|
|
1321
|
+
Any data coming back from the service, will be provided on `response.data` and
|
|
1322
|
+
may be mutated through service endpoint mutations, just as for [`GET`](#get)
|
|
1323
|
+
actions.
|
|
1070
1324
|
|
|
1071
|
-
Example SET_META action:
|
|
1325
|
+
Example `SET_META` action:
|
|
1072
1326
|
|
|
1073
1327
|
```javascript
|
|
1074
1328
|
{
|
|
@@ -1083,37 +1337,35 @@ Example SET_META action:
|
|
|
1083
1337
|
```
|
|
1084
1338
|
|
|
1085
1339
|
Note that the service must be set up to handle metadata. See
|
|
1086
|
-
[Configuring metadata](#configuring-metadata) for more.
|
|
1340
|
+
[Configuring metadata](#configuring-service-metadata) for more.
|
|
1087
1341
|
|
|
1088
1342
|
#### `DELETE` / `DEL`
|
|
1089
1343
|
|
|
1090
|
-
Delete
|
|
1091
|
-
|
|
1344
|
+
Delete one or more items from a service. Set the data for the items to delete,
|
|
1345
|
+
in the payload `data` property as an array of [typed data](#typed-data).
|
|
1346
|
+
In most cases, you only need to provide the `id` and the `$type`, but the way
|
|
1347
|
+
you set up the service may require more properties.
|
|
1092
1348
|
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
relationships are not required.
|
|
1349
|
+
Any data coming back from the service, will be provided on `response.data` and
|
|
1350
|
+
may be mutated through service endpoint mutations, just as for [`GET`](#get)
|
|
1351
|
+
actions.
|
|
1097
1352
|
|
|
1098
|
-
Example DELETE action:
|
|
1353
|
+
Example `DELETE` action:
|
|
1099
1354
|
|
|
1100
1355
|
```javascript
|
|
1101
1356
|
{
|
|
1102
1357
|
type: 'DELETE',
|
|
1103
1358
|
payload: {
|
|
1104
|
-
|
|
1359
|
+
type: 'article',
|
|
1105
1360
|
data: [
|
|
1106
|
-
{id: '
|
|
1107
|
-
{id: '
|
|
1361
|
+
{ id: '12345', $type: 'article' },
|
|
1362
|
+
{ id: '12346', $type: 'article' }
|
|
1108
1363
|
]
|
|
1109
1364
|
}
|
|
1110
1365
|
}
|
|
1111
1366
|
```
|
|
1112
1367
|
|
|
1113
|
-
|
|
1114
|
-
`type` to infer the service from is also possible.
|
|
1115
|
-
|
|
1116
|
-
Example DELETE action for one item:
|
|
1368
|
+
You may also `DELETE` one item like this:
|
|
1117
1369
|
|
|
1118
1370
|
```javascript
|
|
1119
1371
|
{
|
|
@@ -1125,63 +1377,84 @@ Example DELETE action for one item:
|
|
|
1125
1377
|
}
|
|
1126
1378
|
```
|
|
1127
1379
|
|
|
1128
|
-
|
|
1129
|
-
endpoint id is supplied as an `endpoint` property of `payload`.
|
|
1380
|
+
`DEL` is a shorthand for `DELETE`.
|
|
1130
1381
|
|
|
1131
|
-
|
|
1132
|
-
`DELETE` for the `id` and `type` option, but may be overridden on the endpoint.
|
|
1382
|
+
#### `RUN`
|
|
1133
1383
|
|
|
1134
|
-
`
|
|
1384
|
+
The `RUN` action will run jobs provided to `Integreat.create()` in the jobs
|
|
1385
|
+
definitions. These jobs will then run other actions or series of action, also
|
|
1386
|
+
called "flows".
|
|
1387
|
+
|
|
1388
|
+
Only one payload property is required – the `jobId`, which refers to a job in
|
|
1389
|
+
the jobs definitions. Any other properties on the payload will be passed on as
|
|
1390
|
+
input to the job.
|
|
1391
|
+
|
|
1392
|
+
An action for running the `archiveOutdated` job:
|
|
1393
|
+
|
|
1394
|
+
```javascript
|
|
1395
|
+
{
|
|
1396
|
+
type: 'RUN',
|
|
1397
|
+
payload: { jobId: 'archiveOutdated' }
|
|
1398
|
+
}
|
|
1399
|
+
```
|
|
1400
|
+
|
|
1401
|
+
See [the section on jobs](#jobs) for more on how to configure jobs.
|
|
1135
1402
|
|
|
1136
1403
|
#### `SYNC`
|
|
1137
1404
|
|
|
1138
|
-
The `SYNC` action will
|
|
1405
|
+
The `SYNC` action will `GET` items from one service and `SET` them on another.
|
|
1139
1406
|
There are different options for how to retrieve items, ranging from a crude
|
|
1140
1407
|
retrieval of all items on every sync, to a more fine grained approach where only
|
|
1141
|
-
items that have been updated since last sync, will be synced.
|
|
1408
|
+
items that have been updated or created since last sync, will be synced.
|
|
1142
1409
|
|
|
1143
1410
|
The simplest action definition would look like this, where all items would be
|
|
1144
1411
|
retrieved from the service and set on the target:
|
|
1145
1412
|
|
|
1146
|
-
```
|
|
1413
|
+
```javascript
|
|
1147
1414
|
{
|
|
1148
1415
|
type: 'SYNC',
|
|
1149
1416
|
payload: {
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1417
|
+
type: <item type>,
|
|
1418
|
+
retrieve: 'all',
|
|
1419
|
+
from: <service id | payload>,
|
|
1420
|
+
to: <service id | payload>
|
|
1154
1421
|
}
|
|
1155
1422
|
}
|
|
1156
1423
|
```
|
|
1157
1424
|
|
|
1158
|
-
The action will dispatch a
|
|
1425
|
+
The action will dispatch a `GET` action right away, and then immediately
|
|
1159
1426
|
dispatch a `SET_META` action to update the `lastSyncedAt` date on the service.
|
|
1160
|
-
The actions to update the target is added to the queue
|
|
1427
|
+
The `SET` actions to update the target service is added to the queue if one is
|
|
1428
|
+
configured.
|
|
1161
1429
|
|
|
1162
1430
|
To retrieve only new items, change the `retrieve` property to `updated`. In
|
|
1163
|
-
this case, the action will
|
|
1164
|
-
get only newer items, by passing it the `updatedAfter`
|
|
1165
|
-
also filter out older items, in case the service does not
|
|
1431
|
+
this case, the action will dispatch `GET_META` to get the `lastSyncedAt` from
|
|
1432
|
+
the `from` service, and get only newer items, by passing it the `updatedAfter`
|
|
1433
|
+
param. The action will also filter out older items, in case the service does not
|
|
1434
|
+
support `updatedAfter`.
|
|
1435
|
+
|
|
1436
|
+
By setting `retrieve` to `created`, you accomplish the same, but with
|
|
1437
|
+
`createdAfter`.
|
|
1166
1438
|
|
|
1167
1439
|
If you need to include more params in the actions to get from the `from` service
|
|
1168
1440
|
or set to the `to` service, you may provide a params object for the `from` or
|
|
1169
|
-
`to` props, with the service id set as a `service` param.
|
|
1441
|
+
`to` props, with the service id set as a `service` param. You may also provide
|
|
1442
|
+
different action types than `GET` and `SET`, by setting the `action` prop on
|
|
1443
|
+
the `from` or `to` objects respectively.
|
|
1444
|
+
|
|
1445
|
+
> There are more options than these, and the documentation will be updated to
|
|
1446
|
+
> include them later.
|
|
1170
1447
|
|
|
1171
1448
|
#### `EXPIRE`
|
|
1172
1449
|
|
|
1173
|
-
|
|
1174
|
-
these and delete them from the service. The endpoint may include param for the
|
|
1175
|
-
current time, either as microseconds since Januar 1, 1970 UTC with param
|
|
1176
|
-
`{timestamp}` or as the current time in the extended ISO 8601 format
|
|
1177
|
-
(`YYYY-MM-DDThh:mm:ss.sssZ`) with the `{isodate}` param. To get a time in the
|
|
1178
|
-
future instead, set `msFromNow` to a positive number of milliseconds to add
|
|
1179
|
-
to the current time, or set `msFromNow` to a negative number to a time in the
|
|
1180
|
-
past.
|
|
1450
|
+
> Note: This action will change before we reach v1.0.
|
|
1181
1451
|
|
|
1182
|
-
|
|
1452
|
+
The `EXPIRE` action will `GET` expired data items from a service, and the then
|
|
1453
|
+
`DELETE` them.
|
|
1183
1454
|
|
|
1184
|
-
|
|
1455
|
+
Here's an example of an `EXPIRE` action:
|
|
1456
|
+
|
|
1457
|
+
```javascript
|
|
1185
1458
|
{
|
|
1186
1459
|
type: 'EXPIRE',
|
|
1187
1460
|
payload: {
|
|
@@ -1193,21 +1466,40 @@ Here's a typical action definition:
|
|
|
1193
1466
|
}
|
|
1194
1467
|
```
|
|
1195
1468
|
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1469
|
+
The `endpoint` property is required for this action, and needs to specify a
|
|
1470
|
+
service endpoint used to fetch expired items. The action dispatched to this
|
|
1471
|
+
endpoint will have a `timestamp` property with the current time as microseconds
|
|
1472
|
+
since epoc (Januar 1, 1970 UTC), and `isodate` as the current time in the
|
|
1473
|
+
extended ISO 8601 format(`YYYY-MM-DDThh:mm:ss.sssZ`).
|
|
1474
|
+
|
|
1475
|
+
To have `timestamp` and `isodate` be a time in the future instead, set
|
|
1476
|
+
`msFromNow` to a positive number of milliseconds. This will be added to the
|
|
1477
|
+
current time. To have a time in the past, use a negative number for `msFromNow`.
|
|
1200
1478
|
|
|
1201
|
-
|
|
1479
|
+
#### `SERVICE`
|
|
1480
|
+
|
|
1481
|
+
A `SERVICE` action will be sent directly to the specified service without any
|
|
1482
|
+
intervention by Integreat. This allows for running specialized actions on the
|
|
1483
|
+
service that goes beyond what Integreat supports. It's up to each transporter to
|
|
1484
|
+
support such actions, describe what they'll do, and define their payload
|
|
1485
|
+
properties.
|
|
1486
|
+
|
|
1487
|
+
An example of an action that will tell a
|
|
1488
|
+
[Bull](https://github.com/integreat-io/integreat-transporter-bull) queue to
|
|
1489
|
+
clean out all completed jobs more than a week old:
|
|
1202
1490
|
|
|
1203
1491
|
```javascript
|
|
1204
1492
|
{
|
|
1205
|
-
|
|
1206
|
-
|
|
1493
|
+
type: 'SERVICE',
|
|
1494
|
+
payload: {
|
|
1495
|
+
type: 'cleanCompleted',
|
|
1496
|
+
targetService: 'bullService',
|
|
1497
|
+
olderThanMs: 604800000
|
|
1498
|
+
}
|
|
1207
1499
|
}
|
|
1208
1500
|
```
|
|
1209
1501
|
|
|
1210
|
-
###
|
|
1502
|
+
### Write your own action handlers
|
|
1211
1503
|
|
|
1212
1504
|
You may write your own action handlers to handle dispatched actions just like
|
|
1213
1505
|
the built-in types.
|
|
@@ -1215,190 +1507,252 @@ the built-in types.
|
|
|
1215
1507
|
Action handler signature:
|
|
1216
1508
|
|
|
1217
1509
|
```javascript
|
|
1218
|
-
function (
|
|
1510
|
+
async function (action, { dispatch, getService, setProgress, options }) { ... }
|
|
1219
1511
|
```
|
|
1220
1512
|
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
`
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1513
|
+
- `action`: This is the dispatched action after it has been modified a bit
|
|
1514
|
+
by the `dispatch()` method and possible after running an incoming mutation on
|
|
1515
|
+
it. The modifications include cleaning up alias fields (e.g. `service` will be
|
|
1516
|
+
set as `targetService`), removing sensitive or forbidden fields, and setting a
|
|
1517
|
+
few default or internal fields (like the `dispatchedAt` meta).
|
|
1518
|
+
- `dispatch`: From the handler, you may dispatch your own sub actions to the
|
|
1519
|
+
provided `dispatch()` method. Note that this is an "internal dispatch method",
|
|
1520
|
+
so it will return an action with the `response` object on it, instead of just
|
|
1521
|
+
the `response` object. It's good practice to set the `cid` meta prop for the
|
|
1522
|
+
actions you dispatch, to the `cid` meta prop on the `action` you're handling.
|
|
1523
|
+
You should also use the same `ident` unless you have very good reasons to do
|
|
1524
|
+
otherwise, to make sure you don't create security holes.
|
|
1525
|
+
- `getService`: This is a convenience method that will return the relevant
|
|
1526
|
+
service object when you provide it with a type and optional a service id. With
|
|
1527
|
+
a service id, you'll get the service with that id, with only the type, you'll
|
|
1528
|
+
get the default service for that type. E.g.: `getService('article')`.
|
|
1529
|
+
- `setProgress`: For long running tasks, you may want to set the progress along
|
|
1530
|
+
the way. Progress is specified as a number between `0` and `1`, e.g.
|
|
1531
|
+
`setProgress(.5)` to signal that you're halfway through. When the your handler
|
|
1532
|
+
is finished, the progress will automatically be set to `1`. This may be used
|
|
1533
|
+
by queue implementations etc., to give progress feedback to users and to know
|
|
1534
|
+
the action has not gone stale.
|
|
1535
|
+
- `options`: This is an object with a few settings: `queueService` is the id of
|
|
1536
|
+
the service set up as the default queue, and `identConfig` is the config
|
|
1537
|
+
object used for mapping ident schemas to ids, roles, and tokens (see
|
|
1538
|
+
[the `completeIdent` middleware](#completeIdent-middleware)).
|
|
1539
|
+
|
|
1540
|
+
Your action handler must return the `action` it received with a `response`
|
|
1541
|
+
object set on it. If your handler just relays to another action handler, it may
|
|
1542
|
+
reuse the `response` from that handler, but in many cases it will be more
|
|
1543
|
+
correct to generate your own response, possibly based on what the actions you
|
|
1544
|
+
dispatch returns.
|
|
1545
|
+
|
|
1546
|
+
You provide your custom actions to Integreat on setup, by providing an object
|
|
1547
|
+
with the key set to the action type your handler will be responsible for, and
|
|
1548
|
+
the handler function as the value:
|
|
1237
1549
|
|
|
1238
1550
|
```javascript
|
|
1239
1551
|
const actions = {
|
|
1240
|
-
`
|
|
1552
|
+
`MY_ACTION`: async function myAction (action, { dispatch }) { ... }
|
|
1241
1553
|
}
|
|
1242
|
-
const great = Integreat.create(defs, {schemas, services, mappings, actions})
|
|
1554
|
+
const great = Integreat.create(defs, { schemas, services, mappings, actions })
|
|
1243
1555
|
```
|
|
1244
1556
|
|
|
1245
|
-
Note that if
|
|
1246
|
-
already
|
|
1557
|
+
Note that if you set up your custom action handler with an action type that is
|
|
1558
|
+
already used by one of Integreat's built-in action handlers, the custom handler
|
|
1247
1559
|
will have precedence. So be careful when you choose an action type, if your
|
|
1248
1560
|
intention is not to replace an existing action handler.
|
|
1249
1561
|
|
|
1250
|
-
##
|
|
1562
|
+
## Queues
|
|
1251
1563
|
|
|
1252
|
-
|
|
1564
|
+
As everything else in Integreat, a queue is also a service. You configure a
|
|
1565
|
+
queue service, e.g.
|
|
1566
|
+
[`integreat-transporter-bull`](https://github.com/integreat-io/integreat-transporter-bull),
|
|
1567
|
+
and set its service id on the `queueService` property of the definition object
|
|
1568
|
+
you give to `Integreat.create()`:
|
|
1253
1569
|
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1570
|
+
```javascript
|
|
1571
|
+
import bullQueue from `integreat-transporter-bull`
|
|
1572
|
+
|
|
1573
|
+
const services = [
|
|
1574
|
+
{
|
|
1575
|
+
id: 'queue',
|
|
1576
|
+
transporter: 'bull',
|
|
1577
|
+
// ...
|
|
1578
|
+
}
|
|
1579
|
+
]
|
|
1580
|
+
const transporters = {
|
|
1581
|
+
bull: bullQueue
|
|
1582
|
+
}
|
|
1258
1583
|
|
|
1259
|
-
|
|
1584
|
+
const great = Integreat.create(
|
|
1585
|
+
{ services, queueService: 'queue' },
|
|
1586
|
+
{ transporters }
|
|
1587
|
+
)
|
|
1588
|
+
```
|
|
1260
1589
|
|
|
1261
|
-
|
|
1262
|
-
|
|
1590
|
+
To queue an action instead of dispatching it right away, you set `queue: true`
|
|
1591
|
+
on the `meta` object. If everything is set up correctly, Integreat will push the
|
|
1592
|
+
action to the queue. When the action is later pulled from the queue, it will be
|
|
1593
|
+
dispatched again, but without the `queue` property.
|
|
1263
1594
|
|
|
1264
|
-
|
|
1595
|
+
You may also set the meta `queue` property to a Unix timestamp, and if the queue
|
|
1596
|
+
transporter supports it, it will be run at this time instead of being processed
|
|
1597
|
+
as soon as it is next in line in the queue.
|
|
1265
1598
|
|
|
1266
|
-
|
|
1599
|
+
When a queue is not set up, a dispatched action with `queue: true` will just be
|
|
1600
|
+
run right away as a normal action.
|
|
1267
1601
|
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
authenticator: <authenticator id>,
|
|
1272
|
-
options: {
|
|
1273
|
-
...
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
|
-
```
|
|
1602
|
+
You may also use queues directly, by dispatching to it as a server and getting
|
|
1603
|
+
incoming actions from its `listen()` method. In that case, it's just as any
|
|
1604
|
+
other service with no need for any special handling.
|
|
1277
1605
|
|
|
1278
|
-
|
|
1279
|
-
|
|
1606
|
+
> Queueing actions are actually done through an action handler, but this handler
|
|
1607
|
+
> is not available from outside Integreat.
|
|
1280
1608
|
|
|
1281
|
-
##
|
|
1609
|
+
## Middleware
|
|
1282
1610
|
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
- Attribute `transform(value)`
|
|
1611
|
+
Integreat supports middleware, and there are two different middleware
|
|
1612
|
+
"pipelines":
|
|
1286
1613
|
|
|
1287
|
-
|
|
1614
|
+
- The first one is run on dispatched actions. The action goes through the
|
|
1615
|
+
middleware before the action handler takes over, but after the incoming
|
|
1616
|
+
mutations have been run. Because of this, given that you have set up the
|
|
1617
|
+
services with mutation and casting to schemas, you should always be dealing
|
|
1618
|
+
with [typed data](#typed-data) in the middleware.
|
|
1619
|
+
- The action then passes through a second middleware "pipeline" just before it
|
|
1620
|
+
is sent to the service. This happens _after_ all mutations have been run, so
|
|
1621
|
+
you will be dealing with the data as it is sent to the service. Incoming
|
|
1622
|
+
actions from a service also pass through this middleware on the way in,
|
|
1623
|
+
_before_ it is mutated, giving you access to the data as it comes from the
|
|
1624
|
+
service.
|
|
1288
1625
|
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1626
|
+
To set up a logger of what we recieve from and send to a service, you'll use the
|
|
1627
|
+
second middleware "pipeline", while a logger of dispatched actions would be
|
|
1628
|
+
placed in the first.
|
|
1292
1629
|
|
|
1293
|
-
|
|
1630
|
+
When actions pass through middleware, they may modifiy the actions as
|
|
1631
|
+
appropriate, and you will have middleware that modifies (e.g. the
|
|
1632
|
+
[`completeIdent` middleware](#completeident-middleware)), and others that just
|
|
1633
|
+
monitors what's coming through (e.g. a logger).
|
|
1294
1634
|
|
|
1295
|
-
|
|
1635
|
+
Middelware is passed to Integreat like this:
|
|
1296
1636
|
|
|
1637
|
+
```javascript
|
|
1638
|
+
const great = Integreat.create(
|
|
1639
|
+
defs,
|
|
1640
|
+
resources,
|
|
1641
|
+
[
|
|
1642
|
+
// Dispatch middleware
|
|
1643
|
+
],
|
|
1644
|
+
[
|
|
1645
|
+
// Service middleware
|
|
1646
|
+
]
|
|
1647
|
+
)
|
|
1297
1648
|
```
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1649
|
+
|
|
1650
|
+
### `completeIdent` middleware
|
|
1651
|
+
|
|
1652
|
+
If your access rules are based only on the information received from an
|
|
1653
|
+
authenticator, you don't need the following. You will always get an id and
|
|
1654
|
+
potentially some other fields, like roles.
|
|
1655
|
+
|
|
1656
|
+
But when you need to match the ident id from the authenticator with user
|
|
1657
|
+
information held somewhere else, e.g. in a database, you need to configure a
|
|
1658
|
+
user schema and set up a service to fetch this information.
|
|
1659
|
+
|
|
1660
|
+
Integreat uses schemas and services to store idents. In the definition object
|
|
1661
|
+
passed to `Integreat.create()`, you may provide an `identConfig` property with
|
|
1662
|
+
a definition object looking something like this:
|
|
1663
|
+
|
|
1664
|
+
```javascript
|
|
1665
|
+
const great = Integreat.create(
|
|
1666
|
+
{
|
|
1667
|
+
// ...,
|
|
1668
|
+
identConfig: {
|
|
1669
|
+
type: 'user',
|
|
1670
|
+
props: {
|
|
1671
|
+
id: 'id',
|
|
1672
|
+
roles: 'groups',
|
|
1673
|
+
tokens: 'tokens',
|
|
1674
|
+
},
|
|
1675
|
+
},
|
|
1676
|
+
},
|
|
1677
|
+
{
|
|
1678
|
+
// ...
|
|
1679
|
+
}
|
|
1680
|
+
)
|
|
1302
1681
|
```
|
|
1303
1682
|
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
specifies last day of year).
|
|
1322
|
-
- `wm`: Weeks of the month (1-maximum number of weeks in the month, 0 for last
|
|
1323
|
-
week of the month.). First week of the month is the week containing the 1st, and
|
|
1324
|
-
weeks start on Sunday.
|
|
1325
|
-
- `wy`: [ISO weeks of the year](http://en.wikipedia.org/wiki/ISO_week_date)
|
|
1326
|
-
(1-maximum number of ISO weeks in the year, 0 is the last ISO week of the year).
|
|
1327
|
-
- `M`: Months of the year (1-12)
|
|
1328
|
-
- `Y`: Years (1970-2099)
|
|
1329
|
-
|
|
1330
|
-
See Later's documentation on
|
|
1331
|
-
[time periods](http://bunkat.github.io/later/time-periods.html) for more.
|
|
1332
|
-
|
|
1333
|
-
Example schedule running an action at 2 am every weekday:
|
|
1683
|
+
- `type`: This is the id of the schema used for getting ident data. This schema
|
|
1684
|
+
needs to have a `service` specified.
|
|
1685
|
+
- `props`: You may provide alternative field names for the `id`, `roles`, and
|
|
1686
|
+
`tokens` for an ident in the schema specified on `type`. When the prop and the
|
|
1687
|
+
field has the same name, it may be omitted, though it doesn't hurt to specif
|
|
1688
|
+
it anyway for clarity.
|
|
1689
|
+
|
|
1690
|
+
Note that in the example above, the `id` of the data will be used as the ident
|
|
1691
|
+
`id`. When the id is not suited for this, you will need another field on the
|
|
1692
|
+
schema that may act as the ident id. In cases where you need to transform the
|
|
1693
|
+
id from the data in some way, this must be set up as a separate field and the
|
|
1694
|
+
mutation will dictate how to transform it. In most cases, the `id` will do,
|
|
1695
|
+
though.
|
|
1696
|
+
|
|
1697
|
+
For some setups, this requires certain endpoints to be defined on the service.
|
|
1698
|
+
To match a token with an ident, the service must have an endpoint that matches
|
|
1699
|
+
actions like this:
|
|
1334
1700
|
|
|
1335
1701
|
```javascript
|
|
1336
1702
|
{
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
type: '
|
|
1340
|
-
|
|
1341
|
-
from: 'src1',
|
|
1342
|
-
to: 'src2',
|
|
1343
|
-
type: 'entry'
|
|
1344
|
-
}
|
|
1703
|
+
type: 'GET',
|
|
1704
|
+
payload: {
|
|
1705
|
+
type: 'user',
|
|
1706
|
+
tokens: 'twitter|23456'
|
|
1345
1707
|
}
|
|
1346
1708
|
}
|
|
1347
1709
|
```
|
|
1348
1710
|
|
|
1349
|
-
|
|
1711
|
+
In this case, `user` is the schema mapped to idents, and the `tokens`
|
|
1712
|
+
property on the ident is mapped to the `tokens` field on the schema.
|
|
1713
|
+
|
|
1714
|
+
To make Integreat complete idents on actions with the persisted data, set it up
|
|
1715
|
+
with the `completeIdent` middleware:
|
|
1716
|
+
|
|
1717
|
+
```javascript
|
|
1718
|
+
const great = Integreat.create(defs, resources, [
|
|
1719
|
+
Integreat.middleware.completeIdent,
|
|
1720
|
+
])
|
|
1721
|
+
```
|
|
1722
|
+
|
|
1723
|
+
This middleware will intercept any action with `meta.ident` and replace it with
|
|
1724
|
+
the ident item loaded from the designated schema. If the ident has an `id`,
|
|
1725
|
+
the ident with this id is loaded, otherwise a `withToken` is used to load the
|
|
1726
|
+
ident with the specified token. If no ident is found, the original ident is
|
|
1727
|
+
kept.
|
|
1350
1728
|
|
|
1351
1729
|
## Writing middleware
|
|
1352
1730
|
|
|
1353
1731
|
You may write middleware to intercept dispatched actions. This may be useful
|
|
1354
|
-
for logging, debugging, and
|
|
1355
|
-
|
|
1732
|
+
for logging, debugging, and situations where you need to make adjustments to
|
|
1733
|
+
certain actions.
|
|
1356
1734
|
|
|
1357
1735
|
A middleware is a function that accepts a `next()` function as only argument,
|
|
1358
1736
|
and returns an async function that will be called with the action on dispatch.
|
|
1359
1737
|
The returned function is expected to call `next()` with the action, and return
|
|
1360
1738
|
the result from the `next()` function, but is not required to do so. The only
|
|
1361
|
-
requirement is that the functions returns a valid Integreat
|
|
1739
|
+
requirement is that the functions returns a valid Integreat action object,
|
|
1740
|
+
most likely with a `response` object on it.
|
|
1362
1741
|
|
|
1363
1742
|
Example implementation of a very simple logger middleware:
|
|
1364
1743
|
|
|
1365
1744
|
```javascript
|
|
1366
1745
|
const logger = (next) => async (action) => {
|
|
1367
1746
|
console.log('Dispatch was called with action', action)
|
|
1368
|
-
const
|
|
1369
|
-
console.log('Dispatch completed with response', response)
|
|
1370
|
-
return
|
|
1747
|
+
const responseAction = await next(action)
|
|
1748
|
+
console.log('Dispatch completed with response', responseAction.response)
|
|
1749
|
+
return responseAction
|
|
1371
1750
|
}
|
|
1372
1751
|
```
|
|
1373
1752
|
|
|
1374
|
-
## Queue
|
|
1375
|
-
|
|
1376
|
-
Integreat comes with a generic queue interface at `integreat.queue`, that must
|
|
1377
|
-
be setup with a specific queue implementation, for instance
|
|
1378
|
-
[`integreat-queue-redis`](https://github.com/integreat-io/integreat-queue-redis).
|
|
1379
|
-
|
|
1380
|
-
The queue interface is a middleware, that will intercept any dispatched action
|
|
1381
|
-
with `action.meta.queue` set to `true` or a timestamp, and direct it to the
|
|
1382
|
-
queue. When the action is later pulled from the queue, it will be dispatched
|
|
1383
|
-
again, but without the `action.meta.queue` property.
|
|
1384
|
-
|
|
1385
|
-
If a dispatched action has a schedule definition at `action.meta.schedule`, it
|
|
1386
|
-
will be queued for the next timestamp defined by the schedule.
|
|
1387
|
-
|
|
1388
|
-
To setup Integreat with a queue:
|
|
1389
|
-
|
|
1390
|
-
```javascript
|
|
1391
|
-
const queue = Integreat.queue(redisQueue(options))
|
|
1392
|
-
const great = Integreat.create(defs, resources, [queue.middleware])
|
|
1393
|
-
queue.setDispatch(great.dispatch)
|
|
1394
|
-
```
|
|
1395
|
-
|
|
1396
|
-
`queue.middleware` is the middleware, while `queue.setDispatch` must be called
|
|
1397
|
-
to tell the queue interface where to dispatch actions pulled from the queue.
|
|
1398
|
-
|
|
1399
1753
|
## Debugging
|
|
1400
1754
|
|
|
1401
1755
|
Run Integreat with env variable `DEBUG=great`, to receive debug messages.
|
|
1402
1756
|
|
|
1403
|
-
Some sub modules sends debug messages with the `
|
|
1404
|
-
`DEBUG=great,
|
|
1757
|
+
Some sub modules sends debug messages with the `integreat:` prefix, so use
|
|
1758
|
+
`DEBUG=great,integreat:*` to catch these as well.
|