@memberjunction/server 6.1.0-edge.0 → 6.1.0-edge.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generated/generated.d.ts +163 -642
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +894 -3752
- package/dist/generated/generated.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -2
- package/dist/index.js.map +1 -1
- package/dist/resolvers/TaskGraphFrameResolver.d.ts +85 -0
- package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -0
- package/dist/resolvers/TaskGraphFrameResolver.js +163 -0
- package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -0
- package/dist/rest/RESTEndpointHandler.d.ts +0 -4
- package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
- package/dist/rest/RESTEndpointHandler.js +1 -15
- package/dist/rest/RESTEndpointHandler.js.map +1 -1
- package/dist/services/StartTaskGraphDispatcher.d.ts +33 -0
- package/dist/services/StartTaskGraphDispatcher.d.ts.map +1 -0
- package/dist/services/StartTaskGraphDispatcher.js +31 -0
- package/dist/services/StartTaskGraphDispatcher.js.map +1 -0
- package/dist/services/TaskGraphAgentRunner.d.ts +15 -0
- package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -0
- package/dist/services/TaskGraphAgentRunner.js +74 -0
- package/dist/services/TaskGraphAgentRunner.js.map +1 -0
- package/dist/services/TaskGraphContinuationDeliverer.d.ts +70 -0
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -0
- package/dist/services/TaskGraphContinuationDeliverer.js +162 -0
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -0
- package/dist/services/TaskGraphProviderFactory.d.ts +39 -0
- package/dist/services/TaskGraphProviderFactory.d.ts.map +1 -0
- package/dist/services/TaskGraphProviderFactory.js +28 -0
- package/dist/services/TaskGraphProviderFactory.js.map +1 -0
- package/package.json +92 -89
- package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +249 -0
- package/src/__tests__/TaskGraphFrameResolver.test.ts +136 -0
- package/src/generated/generated.ts +623 -2552
- package/src/index.ts +17 -2
- package/src/resolvers/TaskGraphFrameResolver.ts +160 -0
- package/src/rest/RESTEndpointHandler.ts +1 -15
- package/src/services/StartTaskGraphDispatcher.ts +61 -0
- package/src/services/TaskGraphAgentRunner.ts +81 -0
- package/src/services/TaskGraphContinuationDeliverer.ts +181 -0
- package/src/services/TaskGraphProviderFactory.ts +53 -0
- package/dist/resolvers/ReportResolver.d.ts +0 -24
- package/dist/resolvers/ReportResolver.d.ts.map +0 -1
- package/dist/resolvers/ReportResolver.js +0 -198
- package/dist/resolvers/ReportResolver.js.map +0 -1
- package/dist/resolvers/TaskResolver.d.ts +0 -31
- package/dist/resolvers/TaskResolver.d.ts.map +0 -1
- package/dist/resolvers/TaskResolver.js +0 -157
- package/dist/resolvers/TaskResolver.js.map +0 -1
- package/dist/services/TaskOrchestrator.d.ts +0 -130
- package/dist/services/TaskOrchestrator.d.ts.map +0 -1
- package/dist/services/TaskOrchestrator.js +0 -692
- package/dist/services/TaskOrchestrator.js.map +0 -1
- package/src/resolvers/ReportResolver.ts +0 -165
- package/src/resolvers/TaskResolver.ts +0 -148
- package/src/resolvers/__tests__/ReportResolver.test.ts +0 -279
- package/src/services/TaskOrchestrator.ts +0 -873
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the task-graph live signal.
|
|
3
|
+
*
|
|
4
|
+
* `taskGraphFrameFilter` is the security-critical gate and is tested in isolation for the same
|
|
5
|
+
* reason `statusUpdatesFilter` is: a `parentTaskId` is a discoverable value, so without an identity
|
|
6
|
+
* check anyone holding one could watch another user's workflow — including its per-step error
|
|
7
|
+
* messages. It must fail **closed**.
|
|
8
|
+
*
|
|
9
|
+
* The broadcaster's contract is that it can never disturb the work it is describing: a frame is
|
|
10
|
+
* commentary on execution, never a step of it.
|
|
11
|
+
*/
|
|
12
|
+
// type-graphql's decorators evaluate at module load, and they need the metadata polyfill. Without
|
|
13
|
+
// this the file fails to COLLECT — which vitest reports as zero tests rather than as a failure.
|
|
14
|
+
import 'reflect-metadata';
|
|
15
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
16
|
+
|
|
17
|
+
vi.mock('@memberjunction/core', () => ({ LogError: vi.fn() }));
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
TASK_GRAPH_FRAMES_TOPIC,
|
|
21
|
+
TaskGraphFrameBroadcaster,
|
|
22
|
+
taskGraphFrameFilter,
|
|
23
|
+
type TaskGraphFramePayload,
|
|
24
|
+
} from '../resolvers/TaskGraphFrameResolver';
|
|
25
|
+
import type { TaskGraphFrame } from '@memberjunction/task-graph';
|
|
26
|
+
import type { PubSubEngine } from 'type-graphql';
|
|
27
|
+
|
|
28
|
+
const OWNER = '11111111-1111-1111-1111-111111111111';
|
|
29
|
+
const OTHER = '22222222-2222-2222-2222-222222222222';
|
|
30
|
+
const GRAPH = '33333333-3333-3333-3333-333333333333';
|
|
31
|
+
|
|
32
|
+
const payload = (over: Partial<TaskGraphFramePayload> = {}): TaskGraphFramePayload => ({
|
|
33
|
+
Kind: 'TaskCompleted',
|
|
34
|
+
ParentTaskID: GRAPH,
|
|
35
|
+
TaskID: 'task-1',
|
|
36
|
+
TaskName: 'Summarize',
|
|
37
|
+
Status: 'Complete',
|
|
38
|
+
ownerUserId: OWNER,
|
|
39
|
+
...over,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// `null` rather than `undefined` for "no identity": passing `undefined` explicitly triggers a
|
|
43
|
+
// default parameter, so the case would silently test the opposite of what it claims.
|
|
44
|
+
const context = (userId: string | null) => (userId ? { userPayload: { userRecord: { ID: userId } } } : {});
|
|
45
|
+
|
|
46
|
+
const check = (over: Partial<TaskGraphFramePayload> = {}, args = GRAPH, userId: string | null = OWNER) =>
|
|
47
|
+
taskGraphFrameFilter({ payload: payload(over), args: { parentTaskId: args }, context: context(userId) });
|
|
48
|
+
|
|
49
|
+
describe('taskGraphFrameFilter — the delivery gate', () => {
|
|
50
|
+
it('delivers a frame for the watched graph to its owner', () => {
|
|
51
|
+
expect(check()).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('matches graph IDs case-insensitively — UUID casing varies by source', () => {
|
|
55
|
+
expect(check({}, GRAPH.toUpperCase())).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('does NOT deliver frames from another graph', () => {
|
|
59
|
+
expect(check({ ParentTaskID: '44444444-4444-4444-4444-444444444444' })).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('does NOT deliver another user\'s workflow to a subscriber holding its ID', () => {
|
|
63
|
+
// The whole reason identity is checked: parentTaskId is discoverable, and the frames carry
|
|
64
|
+
// per-step error messages.
|
|
65
|
+
expect(check({}, GRAPH, OTHER)).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('fails closed when the connection has no identity', () => {
|
|
69
|
+
expect(check({}, GRAPH, null)).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('fails closed when the frame has no owner', () => {
|
|
73
|
+
expect(check({ ownerUserId: '' as unknown as string })).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('fails closed on a missing graph ID on either side', () => {
|
|
77
|
+
expect(check({ ParentTaskID: '' })).toBe(false);
|
|
78
|
+
expect(check({}, '')).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('TaskGraphFrameBroadcaster', () => {
|
|
83
|
+
let published: Array<[string, unknown]>;
|
|
84
|
+
let pubSub: PubSubEngine;
|
|
85
|
+
|
|
86
|
+
beforeEach(() => {
|
|
87
|
+
published = [];
|
|
88
|
+
pubSub = {
|
|
89
|
+
publish: vi.fn().mockImplementation(async (topic: string, p: unknown) => { published.push([topic, p]); }),
|
|
90
|
+
} as unknown as PubSubEngine;
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const frame = (over: Partial<TaskGraphFrame> = {}): TaskGraphFrame => ({
|
|
94
|
+
Kind: 'TaskStarted', ParentTaskID: GRAPH, OwnerUserID: OWNER, TaskID: 'task-1', TaskName: 'Gather', ...over,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('publishes the frame with the owner attached as the filter key', () => {
|
|
98
|
+
new TaskGraphFrameBroadcaster(pubSub).OnFrame(frame());
|
|
99
|
+
|
|
100
|
+
expect(published).toHaveLength(1);
|
|
101
|
+
const [topic, p] = published[0];
|
|
102
|
+
expect(topic).toBe(TASK_GRAPH_FRAMES_TOPIC);
|
|
103
|
+
expect((p as TaskGraphFramePayload).ownerUserId).toBe(OWNER);
|
|
104
|
+
expect((p as TaskGraphFramePayload).Kind).toBe('TaskStarted');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('drops a frame with no owner rather than publishing one the filter would refuse', () => {
|
|
108
|
+
// Reachable for graphs submitted before ownership was recorded on the parent.
|
|
109
|
+
new TaskGraphFrameBroadcaster(pubSub).OnFrame(frame({ OwnerUserID: null }));
|
|
110
|
+
expect(published).toHaveLength(0);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('never throws when publishing fails — a frame must not disturb the work it describes', () => {
|
|
114
|
+
const exploding = { publish: vi.fn(() => { throw new Error('pubsub down'); }) } as unknown as PubSubEngine;
|
|
115
|
+
expect(() => new TaskGraphFrameBroadcaster(exploding).OnFrame(frame())).not.toThrow();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('carries the whole frame through, so a viewer can render without re-reading rows', () => {
|
|
119
|
+
new TaskGraphFrameBroadcaster(pubSub).OnFrame(
|
|
120
|
+
frame({ Kind: 'TaskFailed', Status: 'Failed', ErrorMessage: 'model timed out' }),
|
|
121
|
+
);
|
|
122
|
+
const p = published[0][1] as TaskGraphFramePayload;
|
|
123
|
+
expect(p.Status).toBe('Failed');
|
|
124
|
+
expect(p.ErrorMessage).toBe('model timed out');
|
|
125
|
+
expect(p.TaskName).toBe('Gather');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('carries graph-level progress on a settled frame', () => {
|
|
129
|
+
new TaskGraphFrameBroadcaster(pubSub).OnFrame(
|
|
130
|
+
frame({ Kind: 'GraphSettled', TaskID: undefined, Status: 'Complete', CompletedCount: 7, TotalCount: 7 }),
|
|
131
|
+
);
|
|
132
|
+
const p = published[0][1] as TaskGraphFramePayload;
|
|
133
|
+
expect(p.CompletedCount).toBe(7);
|
|
134
|
+
expect(p.TotalCount).toBe(7);
|
|
135
|
+
});
|
|
136
|
+
});
|