@dxos/functions 0.5.3-next.57eca40 → 0.5.3-next.63cdcad
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/dist/lib/browser/{chunk-366QG6IX.mjs → chunk-4D4I3YMJ.mjs} +16 -11
- package/dist/lib/browser/chunk-4D4I3YMJ.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +206 -133
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/types.mjs +3 -1
- package/dist/lib/node/{chunk-3VSJ57ZZ.cjs → chunk-3UYUR5N5.cjs} +19 -13
- package/dist/lib/node/chunk-3UYUR5N5.cjs.map +7 -0
- package/dist/lib/node/index.cjs +216 -139
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/types.cjs +6 -4
- package/dist/lib/node/types.cjs.map +2 -2
- package/dist/types/src/browser/index.d.ts +2 -0
- package/dist/types/src/browser/index.d.ts.map +1 -0
- package/dist/types/src/function/function-registry.d.ts.map +1 -1
- package/dist/types/src/handler.d.ts.map +1 -1
- package/dist/types/src/runtime/dev-server.d.ts.map +1 -1
- package/dist/types/src/runtime/scheduler.d.ts +1 -1
- package/dist/types/src/runtime/scheduler.d.ts.map +1 -1
- package/dist/types/src/testing/setup.d.ts.map +1 -1
- package/dist/types/src/trigger/trigger-registry.d.ts +2 -5
- package/dist/types/src/trigger/trigger-registry.d.ts.map +1 -1
- package/dist/types/src/trigger/type/subscription-trigger.d.ts.map +1 -1
- package/dist/types/src/trigger/type/timer-trigger.d.ts.map +1 -1
- package/dist/types/src/trigger/type/webhook-trigger.d.ts.map +1 -1
- package/dist/types/src/trigger/type/websocket-trigger.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +46 -33
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +14 -14
- package/schema/functions.json +5 -0
- package/src/browser/index.ts +5 -0
- package/src/function/function-registry.ts +5 -5
- package/src/runtime/dev-server.ts +3 -3
- package/src/runtime/scheduler.test.ts +14 -9
- package/src/runtime/scheduler.ts +14 -9
- package/src/testing/functions-integration.test.ts +1 -0
- package/src/testing/setup.ts +8 -10
- package/src/trigger/trigger-registry.test.ts +30 -13
- package/src/trigger/trigger-registry.ts +59 -37
- package/src/trigger/type/subscription-trigger.ts +13 -7
- package/src/trigger/type/timer-trigger.ts +4 -3
- package/src/trigger/type/webhook-trigger.ts +3 -2
- package/src/trigger/type/websocket-trigger.ts +4 -3
- package/src/types.ts +42 -30
- package/dist/lib/browser/chunk-366QG6IX.mjs.map +0 -7
- package/dist/lib/node/chunk-3VSJ57ZZ.cjs.map +0 -7
- package/dist/types/src/util.d.ts +0 -15
- package/dist/types/src/util.d.ts.map +0 -1
- package/dist/types/src/util.test.d.ts +0 -2
- package/dist/types/src/util.test.d.ts.map +0 -1
- package/src/util.test.ts +0 -43
- package/src/util.ts +0 -48
|
@@ -6,7 +6,7 @@ import { S } from '@dxos/echo-schema';
|
|
|
6
6
|
* https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions
|
|
7
7
|
*/
|
|
8
8
|
export type FunctionTriggerType = 'subscription' | 'timer' | 'webhook' | 'websocket';
|
|
9
|
-
declare const SubscriptionTriggerSchema: S.struct<{
|
|
9
|
+
declare const SubscriptionTriggerSchema: S.mutable<S.struct<{
|
|
10
10
|
type: S.literal<["subscription"]>;
|
|
11
11
|
filter: S.array<S.struct<{
|
|
12
12
|
type: S.$string;
|
|
@@ -23,12 +23,12 @@ declare const SubscriptionTriggerSchema: S.struct<{
|
|
|
23
23
|
readonly deep?: boolean | undefined;
|
|
24
24
|
readonly delay?: number | undefined;
|
|
25
25
|
} | undefined, never>;
|
|
26
|
-
}
|
|
26
|
+
}>>;
|
|
27
27
|
export type SubscriptionTrigger = S.Schema.Type<typeof SubscriptionTriggerSchema>;
|
|
28
|
-
declare const TimerTriggerSchema: S.struct<{
|
|
28
|
+
declare const TimerTriggerSchema: S.mutable<S.struct<{
|
|
29
29
|
type: S.literal<["timer"]>;
|
|
30
30
|
cron: S.$string;
|
|
31
|
-
}
|
|
31
|
+
}>>;
|
|
32
32
|
export type TimerTrigger = S.Schema.Type<typeof TimerTriggerSchema>;
|
|
33
33
|
declare const WebhookTriggerSchema: S.mutable<S.struct<{
|
|
34
34
|
type: S.literal<["webhook"]>;
|
|
@@ -36,7 +36,7 @@ declare const WebhookTriggerSchema: S.mutable<S.struct<{
|
|
|
36
36
|
port: S.PropertySignature<"?:", number | undefined, never, "?:", number | undefined, never>;
|
|
37
37
|
}>>;
|
|
38
38
|
export type WebhookTrigger = S.Schema.Type<typeof WebhookTriggerSchema>;
|
|
39
|
-
declare const WebsocketTriggerSchema: S.struct<{
|
|
39
|
+
declare const WebsocketTriggerSchema: S.mutable<S.struct<{
|
|
40
40
|
type: S.literal<["websocket"]>;
|
|
41
41
|
url: S.$string;
|
|
42
42
|
init: S.PropertySignature<"?:", {
|
|
@@ -44,7 +44,7 @@ declare const WebsocketTriggerSchema: S.struct<{
|
|
|
44
44
|
} | undefined, never, "?:", {
|
|
45
45
|
readonly [x: string]: any;
|
|
46
46
|
} | undefined, never>;
|
|
47
|
-
}
|
|
47
|
+
}>>;
|
|
48
48
|
export type WebsocketTrigger = S.Schema.Type<typeof WebsocketTriggerSchema>;
|
|
49
49
|
export type TriggerSpec = TimerTrigger | WebhookTrigger | WebsocketTrigger | SubscriptionTrigger;
|
|
50
50
|
declare const FunctionDef_base: import("@dxos/echo-schema").EchoSchemaClass<{
|
|
@@ -62,36 +62,42 @@ export declare class FunctionDef extends FunctionDef_base {
|
|
|
62
62
|
}
|
|
63
63
|
declare const FunctionTrigger_base: import("@dxos/echo-schema").EchoSchemaClass<{
|
|
64
64
|
function: string;
|
|
65
|
-
|
|
65
|
+
enabled?: boolean | undefined;
|
|
66
|
+
meta?: {
|
|
67
|
+
[x: string]: any;
|
|
68
|
+
} | undefined;
|
|
66
69
|
spec: {
|
|
67
|
-
|
|
70
|
+
filter: readonly {
|
|
68
71
|
readonly type: string;
|
|
69
72
|
readonly props?: {
|
|
70
73
|
readonly [x: string]: any;
|
|
71
74
|
} | undefined;
|
|
72
75
|
}[];
|
|
73
|
-
|
|
74
|
-
|
|
76
|
+
type: "subscription";
|
|
77
|
+
options?: {
|
|
75
78
|
readonly deep?: boolean | undefined;
|
|
76
79
|
readonly delay?: number | undefined;
|
|
77
80
|
} | undefined;
|
|
78
81
|
} | {
|
|
79
|
-
|
|
80
|
-
|
|
82
|
+
type: "timer";
|
|
83
|
+
cron: string;
|
|
81
84
|
} | {
|
|
82
85
|
port?: number | undefined;
|
|
83
86
|
type: "webhook";
|
|
84
87
|
method: string;
|
|
85
88
|
} | {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
+
type: "websocket";
|
|
90
|
+
url: string;
|
|
91
|
+
init?: {
|
|
89
92
|
readonly [x: string]: any;
|
|
90
93
|
} | undefined;
|
|
91
94
|
};
|
|
92
95
|
} & {
|
|
93
96
|
id: string;
|
|
94
97
|
}>;
|
|
98
|
+
/**
|
|
99
|
+
* Function trigger.
|
|
100
|
+
*/
|
|
95
101
|
export declare class FunctionTrigger extends FunctionTrigger_base {
|
|
96
102
|
}
|
|
97
103
|
/**
|
|
@@ -125,30 +131,33 @@ export declare const FunctionManifestSchema: S.struct<{
|
|
|
125
131
|
})[] | undefined, never>;
|
|
126
132
|
triggers: S.PropertySignature<"?:", ({
|
|
127
133
|
function: string;
|
|
128
|
-
|
|
134
|
+
enabled?: boolean | undefined;
|
|
135
|
+
meta?: {
|
|
136
|
+
[x: string]: any;
|
|
137
|
+
} | undefined;
|
|
129
138
|
spec: {
|
|
130
|
-
|
|
139
|
+
filter: readonly {
|
|
131
140
|
readonly type: string;
|
|
132
141
|
readonly props?: {
|
|
133
142
|
readonly [x: string]: any;
|
|
134
143
|
} | undefined;
|
|
135
144
|
}[];
|
|
136
|
-
|
|
137
|
-
|
|
145
|
+
type: "subscription";
|
|
146
|
+
options?: {
|
|
138
147
|
readonly deep?: boolean | undefined;
|
|
139
148
|
readonly delay?: number | undefined;
|
|
140
149
|
} | undefined;
|
|
141
150
|
} | {
|
|
142
|
-
|
|
143
|
-
|
|
151
|
+
type: "timer";
|
|
152
|
+
cron: string;
|
|
144
153
|
} | {
|
|
145
154
|
port?: number | undefined;
|
|
146
155
|
type: "webhook";
|
|
147
156
|
method: string;
|
|
148
157
|
} | {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
158
|
+
type: "websocket";
|
|
159
|
+
url: string;
|
|
160
|
+
init?: {
|
|
152
161
|
readonly [x: string]: any;
|
|
153
162
|
} | undefined;
|
|
154
163
|
};
|
|
@@ -161,30 +170,33 @@ export declare const FunctionManifestSchema: S.struct<{
|
|
|
161
170
|
} | undefined;
|
|
162
171
|
})[] | undefined, never, "?:", ({
|
|
163
172
|
function: string;
|
|
164
|
-
|
|
173
|
+
enabled?: boolean | undefined;
|
|
174
|
+
meta?: {
|
|
175
|
+
[x: string]: any;
|
|
176
|
+
} | undefined;
|
|
165
177
|
spec: {
|
|
166
|
-
|
|
178
|
+
filter: readonly {
|
|
167
179
|
readonly type: string;
|
|
168
180
|
readonly props?: {
|
|
169
181
|
readonly [x: string]: any;
|
|
170
182
|
} | undefined;
|
|
171
183
|
}[];
|
|
172
|
-
|
|
173
|
-
|
|
184
|
+
type: "subscription";
|
|
185
|
+
options?: {
|
|
174
186
|
readonly deep?: boolean | undefined;
|
|
175
187
|
readonly delay?: number | undefined;
|
|
176
188
|
} | undefined;
|
|
177
189
|
} | {
|
|
178
|
-
|
|
179
|
-
|
|
190
|
+
type: "timer";
|
|
191
|
+
cron: string;
|
|
180
192
|
} | {
|
|
181
193
|
port?: number | undefined;
|
|
182
194
|
type: "webhook";
|
|
183
195
|
method: string;
|
|
184
196
|
} | {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
197
|
+
type: "websocket";
|
|
198
|
+
url: string;
|
|
199
|
+
init?: {
|
|
188
200
|
readonly [x: string]: any;
|
|
189
201
|
} | undefined;
|
|
190
202
|
};
|
|
@@ -198,5 +210,6 @@ export declare const FunctionManifestSchema: S.struct<{
|
|
|
198
210
|
})[] | undefined, never>;
|
|
199
211
|
}>;
|
|
200
212
|
export type FunctionManifest = S.Schema.Type<typeof FunctionManifestSchema>;
|
|
213
|
+
export declare const FUNCTION_SCHEMA: (typeof FunctionDef | typeof FunctionTrigger)[];
|
|
201
214
|
export {};
|
|
202
215
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAIA,OAAO,EAAa,CAAC,EAAe,MAAM,mBAAmB,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,cAAc,GAAG,OAAO,GAAG,SAAS,GAAG,WAAW,CAAC;AAErF,QAAA,MAAM,yBAAyB;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAIA,OAAO,EAAa,CAAC,EAAe,MAAM,mBAAmB,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,cAAc,GAAG,OAAO,GAAG,SAAS,GAAG,WAAW,CAAC;AAErF,QAAA,MAAM,yBAAyB;;;;;;;;;;;;;;;;;GAmB9B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAElF,QAAA,MAAM,kBAAkB;;;GAKvB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,QAAA,MAAM,oBAAoB;;;;GAOzB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE,QAAA,MAAM,sBAAsB;;;;;;;;GAM3B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAS5E,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,cAAc,GAAG,gBAAgB,GAAG,mBAAmB,CAAC;;;;;;;;;AAEjG;;GAEG;AACH,qBAAa,WAAY,SAAQ,gBAQ/B;CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEL;;GAEG;AACH,qBAAa,eAAgB,SAAQ,oBASnC;CAAG;AAEL;;GAEG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGjC,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAG5E,eAAO,MAAM,eAAe,iDAAiC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/functions",
|
|
3
|
-
"version": "0.5.3-next.
|
|
3
|
+
"version": "0.5.3-next.63cdcad",
|
|
4
4
|
"description": "Functions API and runtime.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -40,23 +40,23 @@
|
|
|
40
40
|
"express": "^4.19.2",
|
|
41
41
|
"get-port-please": "^3.1.1",
|
|
42
42
|
"ws": "^8.14.2",
|
|
43
|
-
"@braneframe/types": "0.5.3-next.
|
|
44
|
-
"@dxos/
|
|
45
|
-
"@dxos/context": "0.5.3-next.
|
|
46
|
-
"@dxos/
|
|
47
|
-
"@dxos/
|
|
48
|
-
"@dxos/invariant": "0.5.3-next.
|
|
49
|
-
"@dxos/
|
|
50
|
-
"@dxos/
|
|
51
|
-
"@dxos/
|
|
52
|
-
"@dxos/
|
|
53
|
-
"@dxos/
|
|
54
|
-
"@dxos/
|
|
43
|
+
"@braneframe/types": "0.5.3-next.63cdcad",
|
|
44
|
+
"@dxos/async": "0.5.3-next.63cdcad",
|
|
45
|
+
"@dxos/context": "0.5.3-next.63cdcad",
|
|
46
|
+
"@dxos/client": "0.5.3-next.63cdcad",
|
|
47
|
+
"@dxos/echo-db": "0.5.3-next.63cdcad",
|
|
48
|
+
"@dxos/invariant": "0.5.3-next.63cdcad",
|
|
49
|
+
"@dxos/keys": "0.5.3-next.63cdcad",
|
|
50
|
+
"@dxos/log": "0.5.3-next.63cdcad",
|
|
51
|
+
"@dxos/node-std": "0.5.3-next.63cdcad",
|
|
52
|
+
"@dxos/echo-schema": "0.5.3-next.63cdcad",
|
|
53
|
+
"@dxos/protocols": "0.5.3-next.63cdcad",
|
|
54
|
+
"@dxos/util": "0.5.3-next.63cdcad"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@types/express": "^4.17.17",
|
|
58
58
|
"@types/ws": "^7.4.0",
|
|
59
|
-
"@dxos/agent": "0.5.3-next.
|
|
59
|
+
"@dxos/agent": "0.5.3-next.63cdcad"
|
|
60
60
|
},
|
|
61
61
|
"publishConfig": {
|
|
62
62
|
"access": "public"
|
package/schema/functions.json
CHANGED
|
@@ -8,10 +8,9 @@ import { create, Filter, type Space } from '@dxos/client/echo';
|
|
|
8
8
|
import { type Context, Resource } from '@dxos/context';
|
|
9
9
|
import { PublicKey } from '@dxos/keys';
|
|
10
10
|
import { log } from '@dxos/log';
|
|
11
|
-
import { ComplexMap } from '@dxos/util';
|
|
11
|
+
import { ComplexMap, diff } from '@dxos/util';
|
|
12
12
|
|
|
13
13
|
import { FunctionDef, type FunctionManifest } from '../types';
|
|
14
|
-
import { diff } from '../util';
|
|
15
14
|
|
|
16
15
|
export type FunctionsRegisteredEvent = {
|
|
17
16
|
space: Space;
|
|
@@ -46,13 +45,13 @@ export class FunctionRegistry extends Resource {
|
|
|
46
45
|
|
|
47
46
|
// Sync definitions.
|
|
48
47
|
const { objects: existing } = await space.db.query(Filter.schema(FunctionDef)).run();
|
|
49
|
-
const { added
|
|
50
|
-
added.forEach((def) => space.db.add(create(FunctionDef, def)));
|
|
48
|
+
const { added } = diff(existing, functions, (a, b) => a.uri === b.uri);
|
|
51
49
|
// TODO(burdon): Update existing templates.
|
|
52
|
-
|
|
50
|
+
added.forEach((def) => space.db.add(create(FunctionDef, def)));
|
|
53
51
|
}
|
|
54
52
|
|
|
55
53
|
protected override async _open(): Promise<void> {
|
|
54
|
+
log.info('opening...');
|
|
56
55
|
const spacesSubscription = this._client.spaces.subscribe(async (spaces) => {
|
|
57
56
|
for (const space of spaces) {
|
|
58
57
|
if (this._functionBySpaceKey.has(space.key)) {
|
|
@@ -85,6 +84,7 @@ export class FunctionRegistry extends Resource {
|
|
|
85
84
|
}
|
|
86
85
|
|
|
87
86
|
protected override async _close(_: Context): Promise<void> {
|
|
87
|
+
log.info('closing...');
|
|
88
88
|
this._functionBySpaceKey.clear();
|
|
89
89
|
}
|
|
90
90
|
}
|
|
@@ -46,6 +46,7 @@ export class DevServer {
|
|
|
46
46
|
private readonly _functionsRegistry: FunctionRegistry,
|
|
47
47
|
private readonly _options: DevServerOptions,
|
|
48
48
|
) {
|
|
49
|
+
// TODO(burdon): Add/remove listener in start/stop.
|
|
49
50
|
this._functionsRegistry.registered.on(async ({ added }) => {
|
|
50
51
|
added.forEach((def) => this._load(def));
|
|
51
52
|
await this._safeUpdateRegistration();
|
|
@@ -188,8 +189,8 @@ export class DevServer {
|
|
|
188
189
|
registrationId: this._functionServiceRegistration,
|
|
189
190
|
functions: this.functions.map(({ def: { id, route } }) => ({ id, route })),
|
|
190
191
|
});
|
|
191
|
-
} catch (
|
|
192
|
-
log.catch(
|
|
192
|
+
} catch (err) {
|
|
193
|
+
log.catch(err);
|
|
193
194
|
}
|
|
194
195
|
}
|
|
195
196
|
|
|
@@ -211,7 +212,6 @@ export class DevServer {
|
|
|
211
212
|
private async _invoke(path: string, event: FunctionEvent) {
|
|
212
213
|
const { handler } = this._handlers[path] ?? {};
|
|
213
214
|
invariant(handler, `invalid path: ${path}`);
|
|
214
|
-
|
|
215
215
|
const context: FunctionContext = {
|
|
216
216
|
client: this._client,
|
|
217
217
|
dataDir: this._options.dataDir,
|
|
@@ -29,6 +29,15 @@ describe('scheduler', () => {
|
|
|
29
29
|
await testBuilder.destroy();
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
+
const createScheduler = (callback: SchedulerOptions['callback']) => {
|
|
33
|
+
const scheduler = new Scheduler(new FunctionRegistry(client), new TriggerRegistry(client), { callback });
|
|
34
|
+
after(async () => {
|
|
35
|
+
await scheduler.stop();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return scheduler;
|
|
39
|
+
};
|
|
40
|
+
|
|
32
41
|
test('timer', async () => {
|
|
33
42
|
const manifest: FunctionManifest = {
|
|
34
43
|
functions: [
|
|
@@ -41,6 +50,7 @@ describe('scheduler', () => {
|
|
|
41
50
|
triggers: [
|
|
42
51
|
{
|
|
43
52
|
function: 'example.com/function/test',
|
|
53
|
+
enabled: true,
|
|
44
54
|
spec: {
|
|
45
55
|
type: 'timer',
|
|
46
56
|
cron: '0/1 * * * * *', // Every 1s.
|
|
@@ -75,6 +85,7 @@ describe('scheduler', () => {
|
|
|
75
85
|
triggers: [
|
|
76
86
|
{
|
|
77
87
|
function: 'example.com/function/test',
|
|
88
|
+
enabled: true,
|
|
78
89
|
spec: {
|
|
79
90
|
type: 'webhook',
|
|
80
91
|
method: 'GET',
|
|
@@ -108,6 +119,7 @@ describe('scheduler', () => {
|
|
|
108
119
|
triggers: [
|
|
109
120
|
{
|
|
110
121
|
function: 'example.com/function/test',
|
|
122
|
+
enabled: true,
|
|
111
123
|
spec: {
|
|
112
124
|
type: 'websocket',
|
|
113
125
|
// url: 'https://hub.dxos.network/api/mailbox/test',
|
|
@@ -154,6 +166,7 @@ describe('scheduler', () => {
|
|
|
154
166
|
triggers: [
|
|
155
167
|
{
|
|
156
168
|
function: 'example.com/function/test',
|
|
169
|
+
enabled: true,
|
|
157
170
|
spec: {
|
|
158
171
|
type: 'subscription',
|
|
159
172
|
filter: [{ type: TestType.typename }],
|
|
@@ -165,7 +178,7 @@ describe('scheduler', () => {
|
|
|
165
178
|
let count = 0;
|
|
166
179
|
const done = new Trigger();
|
|
167
180
|
const scheduler = createScheduler(async () => {
|
|
168
|
-
if (++count ===
|
|
181
|
+
if (++count === 1) {
|
|
169
182
|
done.wake();
|
|
170
183
|
}
|
|
171
184
|
});
|
|
@@ -180,12 +193,4 @@ describe('scheduler', () => {
|
|
|
180
193
|
|
|
181
194
|
await done.wait();
|
|
182
195
|
});
|
|
183
|
-
|
|
184
|
-
const createScheduler = (callback: SchedulerOptions['callback']) => {
|
|
185
|
-
const scheduler = new Scheduler(new FunctionRegistry(client), new TriggerRegistry(client), { callback });
|
|
186
|
-
after(async () => {
|
|
187
|
-
await scheduler.stop();
|
|
188
|
-
});
|
|
189
|
-
return scheduler;
|
|
190
|
-
};
|
|
191
196
|
});
|
package/src/runtime/scheduler.ts
CHANGED
|
@@ -27,7 +27,7 @@ export type SchedulerOptions = {
|
|
|
27
27
|
export class Scheduler {
|
|
28
28
|
private _ctx = createContext();
|
|
29
29
|
|
|
30
|
-
private readonly
|
|
30
|
+
private readonly _functionUriToCallMutex = new Map<string, Mutex>();
|
|
31
31
|
|
|
32
32
|
constructor(
|
|
33
33
|
public readonly functions: FunctionRegistry,
|
|
@@ -72,23 +72,28 @@ export class Scheduler {
|
|
|
72
72
|
await Promise.all(mountTasks).catch(log.catch);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
private async activate(space: Space, functions: FunctionDef[],
|
|
76
|
-
const definition = functions.find((def) => def.uri ===
|
|
75
|
+
private async activate(space: Space, functions: FunctionDef[], trigger: FunctionTrigger) {
|
|
76
|
+
const definition = functions.find((def) => def.uri === trigger.function);
|
|
77
77
|
if (!definition) {
|
|
78
|
-
log.info('function is not found for trigger', {
|
|
78
|
+
log.info('function is not found for trigger', { trigger });
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
await this.triggers.activate(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
await this.triggers.activate(space, trigger, async (args) => {
|
|
83
|
+
const mutex = this._functionUriToCallMutex.get(definition.uri) ?? new Mutex();
|
|
84
|
+
this._functionUriToCallMutex.set(definition.uri, mutex);
|
|
85
|
+
|
|
86
|
+
log.info('function triggered, waiting for mutex', { uri: definition.uri });
|
|
87
|
+
return mutex.executeSynchronized(() => {
|
|
88
|
+
log.info('mutex acquired', { uri: definition.uri });
|
|
89
|
+
return this._execFunction(definition, trigger, {
|
|
90
|
+
meta: trigger.meta ?? {},
|
|
86
91
|
data: { ...args, spaceKey: space.key },
|
|
87
92
|
});
|
|
88
93
|
});
|
|
89
94
|
});
|
|
90
95
|
|
|
91
|
-
log('activated trigger', { space: space.key, trigger
|
|
96
|
+
log('activated trigger', { space: space.key, trigger });
|
|
92
97
|
}
|
|
93
98
|
|
|
94
99
|
private async _execFunction<TData, TMeta>(
|
package/src/testing/setup.ts
CHANGED
|
@@ -10,16 +10,16 @@ import { range } from '@dxos/util';
|
|
|
10
10
|
import { TestType } from './types';
|
|
11
11
|
import { FunctionDef, FunctionTrigger } from '../types';
|
|
12
12
|
|
|
13
|
-
// TODO(burdon):
|
|
13
|
+
// TODO(burdon): Extend/wrap TestBuilder.
|
|
14
14
|
|
|
15
15
|
export const createInitializedClients = async (testBuilder: TestBuilder, count: number = 1, config?: Config) => {
|
|
16
16
|
const clients = range(count).map(() => new Client({ config, services: testBuilder.createLocalClientServices() }));
|
|
17
|
-
testBuilder.ctx.onDispose(() => Promise.all(clients.map((
|
|
17
|
+
testBuilder.ctx.onDispose(() => Promise.all(clients.map((client) => client.destroy())));
|
|
18
18
|
return Promise.all(
|
|
19
19
|
clients.map(async (client, index) => {
|
|
20
20
|
await client.initialize();
|
|
21
21
|
await client.halo.createIdentity({ displayName: `Peer ${index}` });
|
|
22
|
-
client.addSchema(
|
|
22
|
+
client.addSchema(FunctionDef, FunctionTrigger, TestType);
|
|
23
23
|
return client;
|
|
24
24
|
}),
|
|
25
25
|
);
|
|
@@ -34,12 +34,10 @@ export const createFunctionRuntime = async (testBuilder: TestBuilder): Promise<C
|
|
|
34
34
|
},
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
-
const client =
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
await functionsPlugin.open();
|
|
43
|
-
testBuilder.ctx.onDispose(() => functionsPlugin.close());
|
|
37
|
+
const [client] = await createInitializedClients(testBuilder, 1, config);
|
|
38
|
+
const plugin = new FunctionsPlugin();
|
|
39
|
+
await plugin.initialize({ client, clientServices: client.services });
|
|
40
|
+
await plugin.open();
|
|
41
|
+
testBuilder.ctx.onDispose(() => plugin.close());
|
|
44
42
|
return client;
|
|
45
43
|
};
|
|
@@ -31,6 +31,7 @@ const manifest: FunctionManifest = {
|
|
|
31
31
|
],
|
|
32
32
|
},
|
|
33
33
|
function: 'example.com/function/webhook-test',
|
|
34
|
+
enabled: true,
|
|
34
35
|
spec: {
|
|
35
36
|
type: 'webhook',
|
|
36
37
|
method: 'GET',
|
|
@@ -46,6 +47,7 @@ const manifest: FunctionManifest = {
|
|
|
46
47
|
],
|
|
47
48
|
},
|
|
48
49
|
function: 'example.com/function/subscription-test',
|
|
50
|
+
enabled: true,
|
|
49
51
|
spec: {
|
|
50
52
|
type: 'subscription',
|
|
51
53
|
filter: [
|
|
@@ -74,7 +76,7 @@ describe('trigger registry', () => {
|
|
|
74
76
|
|
|
75
77
|
describe('register', () => {
|
|
76
78
|
test('creates new triggers', async () => {
|
|
77
|
-
const client =
|
|
79
|
+
const [client] = await createInitializedClients(testBuilder);
|
|
78
80
|
const registry = createRegistry(client);
|
|
79
81
|
const space = await client.spaces.create();
|
|
80
82
|
await registry.register(space, manifest);
|
|
@@ -84,11 +86,24 @@ describe('trigger registry', () => {
|
|
|
84
86
|
const expected = manifest.triggers?.map((trigger) => trigger.function).sort();
|
|
85
87
|
expect(objects.map((object: FunctionTrigger) => object.function).sort()).to.deep.eq(expected);
|
|
86
88
|
});
|
|
89
|
+
|
|
90
|
+
test('set meta', () => {
|
|
91
|
+
const trigger = create(FunctionTrigger, {
|
|
92
|
+
function: 'example.com/function/webhook-test',
|
|
93
|
+
spec: {
|
|
94
|
+
type: 'webhook',
|
|
95
|
+
method: 'GET',
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
(trigger.meta ??= {}).test = 100;
|
|
100
|
+
expect(trigger.meta.test).to.eq(100);
|
|
101
|
+
});
|
|
87
102
|
});
|
|
88
103
|
|
|
89
104
|
describe('activate', () => {
|
|
90
105
|
test('invokes the provided callback', async () => {
|
|
91
|
-
const client =
|
|
106
|
+
const [client] = await createInitializedClients(testBuilder);
|
|
92
107
|
const space = await client.spaces.create();
|
|
93
108
|
const registry = createRegistry(client);
|
|
94
109
|
await registry.register(space, manifest);
|
|
@@ -98,7 +113,7 @@ describe('trigger registry', () => {
|
|
|
98
113
|
const callbackInvoked = new Trigger();
|
|
99
114
|
const { objects: allTriggers } = await space.db.query(Filter.schema(FunctionTrigger)).run();
|
|
100
115
|
const webhookTrigger = allTriggers.find((trigger: FunctionTrigger) => trigger.spec.type === 'webhook')!;
|
|
101
|
-
await registry.activate(
|
|
116
|
+
await registry.activate(space, webhookTrigger, async () => {
|
|
102
117
|
callbackInvoked.wake();
|
|
103
118
|
return 200;
|
|
104
119
|
});
|
|
@@ -108,7 +123,7 @@ describe('trigger registry', () => {
|
|
|
108
123
|
});
|
|
109
124
|
|
|
110
125
|
test('removes from inactive list', async () => {
|
|
111
|
-
const client =
|
|
126
|
+
const [client] = await createInitializedClients(testBuilder);
|
|
112
127
|
const space = await client.spaces.create();
|
|
113
128
|
const registry = createRegistry(client);
|
|
114
129
|
await registry.register(space, manifest);
|
|
@@ -116,17 +131,19 @@ describe('trigger registry', () => {
|
|
|
116
131
|
await waitForInactiveTriggers(registry, space);
|
|
117
132
|
|
|
118
133
|
const inactiveTrigger = registry.getInactiveTriggers(space)[0];
|
|
119
|
-
await registry.activate(
|
|
134
|
+
await registry.activate(space, inactiveTrigger, async () => 200);
|
|
120
135
|
|
|
121
136
|
const updatedInactiveList = registry.getInactiveTriggers(space);
|
|
122
137
|
expect(updatedInactiveList.find((trigger: FunctionTrigger) => trigger.function === inactiveTrigger.function)).to
|
|
123
138
|
.be.undefined;
|
|
124
139
|
});
|
|
140
|
+
|
|
141
|
+
// TODO(burdon): Test enable/disable trigger.
|
|
125
142
|
});
|
|
126
143
|
|
|
127
144
|
describe('deactivate', () => {
|
|
128
145
|
test('trigger object deletion deactivates a trigger', async () => {
|
|
129
|
-
const client =
|
|
146
|
+
const [client] = await createInitializedClients(testBuilder);
|
|
130
147
|
const space = await client.spaces.create();
|
|
131
148
|
const registry = createRegistry(client);
|
|
132
149
|
await registry.register(space, manifest);
|
|
@@ -136,7 +153,7 @@ describe('trigger registry', () => {
|
|
|
136
153
|
const { objects: allTriggers } = await space.db.query(Filter.schema(FunctionTrigger)).run();
|
|
137
154
|
const echoTrigger = allTriggers.find((trigger: FunctionTrigger) => trigger.spec.type === 'subscription')!;
|
|
138
155
|
let count = 0;
|
|
139
|
-
await registry.activate(
|
|
156
|
+
await registry.activate(space, echoTrigger, async () => {
|
|
140
157
|
count++;
|
|
141
158
|
return 200;
|
|
142
159
|
});
|
|
@@ -152,7 +169,7 @@ describe('trigger registry', () => {
|
|
|
152
169
|
});
|
|
153
170
|
|
|
154
171
|
test('registry closing deactivates a trigger', async () => {
|
|
155
|
-
const client =
|
|
172
|
+
const [client] = await createInitializedClients(testBuilder);
|
|
156
173
|
const space = await client.spaces.create();
|
|
157
174
|
const registry = createRegistry(client);
|
|
158
175
|
await registry.register(space, manifest);
|
|
@@ -162,7 +179,7 @@ describe('trigger registry', () => {
|
|
|
162
179
|
const { objects: allTriggers } = await space.db.query(Filter.schema(FunctionTrigger)).run();
|
|
163
180
|
const echoTrigger = allTriggers.find((trigger: FunctionTrigger) => trigger.spec.type === 'subscription')!;
|
|
164
181
|
let count = 0;
|
|
165
|
-
await registry.activate(
|
|
182
|
+
await registry.activate(space, echoTrigger, async () => {
|
|
166
183
|
count++;
|
|
167
184
|
return 200;
|
|
168
185
|
});
|
|
@@ -176,8 +193,8 @@ describe('trigger registry', () => {
|
|
|
176
193
|
});
|
|
177
194
|
|
|
178
195
|
describe('trigger events', () => {
|
|
179
|
-
test
|
|
180
|
-
const client =
|
|
196
|
+
test('event fired when all registered when opened', async () => {
|
|
197
|
+
const [client] = await createInitializedClients(testBuilder);
|
|
181
198
|
const registry = createRegistry(client);
|
|
182
199
|
const triggers = createTriggers(client.spaces.default, 3);
|
|
183
200
|
|
|
@@ -194,7 +211,7 @@ describe('trigger registry', () => {
|
|
|
194
211
|
});
|
|
195
212
|
|
|
196
213
|
test('event fired when a new trigger is added', async () => {
|
|
197
|
-
const client =
|
|
214
|
+
const [client] = await createInitializedClients(testBuilder);
|
|
198
215
|
const registry = createRegistry(client);
|
|
199
216
|
const space = await client.spaces.create();
|
|
200
217
|
|
|
@@ -210,7 +227,7 @@ describe('trigger registry', () => {
|
|
|
210
227
|
});
|
|
211
228
|
|
|
212
229
|
test('event fired when a new trigger is removed', async () => {
|
|
213
|
-
const client =
|
|
230
|
+
const [client] = await createInitializedClients(testBuilder);
|
|
214
231
|
const registry = createRegistry(client);
|
|
215
232
|
const space = await client.spaces.create();
|
|
216
233
|
const triggers = createTriggers(space, 3);
|