@shipfox/api-triggers 6.0.0 → 7.1.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shipfox/api-triggers",
3
3
  "license": "MIT",
4
- "version": "6.0.0",
4
+ "version": "7.1.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/ShipfoxHQ/shipfox.git",
@@ -26,42 +26,25 @@
26
26
  "cron-parser": "^5.6.1",
27
27
  "drizzle-orm": "^0.45.2",
28
28
  "zod": "^4.4.3",
29
- "@shipfox/api-auth-context": "6.0.0",
29
+ "@shipfox/api-auth-context": "7.1.0",
30
30
  "@shipfox/api-definitions-dto": "6.0.0",
31
31
  "@shipfox/api-integration-core-dto": "6.0.0",
32
- "@shipfox/api-projects": "6.0.0",
32
+ "@shipfox/api-projects": "7.1.0",
33
33
  "@shipfox/api-triggers-dto": "5.0.0",
34
34
  "@shipfox/api-workflows-dto": "6.0.0",
35
35
  "@shipfox/config": "1.2.2",
36
36
  "@shipfox/expression": "1.1.3",
37
37
  "@shipfox/inter-module": "0.2.0",
38
38
  "@shipfox/node-drizzle": "0.3.2",
39
- "@shipfox/node-fastify": "0.2.4",
40
- "@shipfox/node-module": "0.4.0",
41
- "@shipfox/node-opentelemetry": "0.5.2",
39
+ "@shipfox/node-error-monitoring": "0.2.0",
40
+ "@shipfox/node-fastify": "0.3.0",
41
+ "@shipfox/node-module": "0.5.0",
42
+ "@shipfox/node-opentelemetry": "0.6.0",
42
43
  "@shipfox/node-outbox": "0.2.4",
43
44
  "@shipfox/node-postgres": "0.4.2",
44
- "@shipfox/node-temporal": "0.3.2",
45
+ "@shipfox/node-temporal": "0.4.0",
45
46
  "@shipfox/workflow-document": "2.1.1"
46
47
  },
47
- "devDependencies": {
48
- "@temporalio/activity": "1.18.1",
49
- "@temporalio/client": "1.18.1",
50
- "@temporalio/common": "1.18.1",
51
- "@temporalio/testing": "1.18.1",
52
- "@temporalio/worker": "1.18.1",
53
- "@types/pg": "^8.15.5",
54
- "drizzle-kit": "^0.31.10",
55
- "fastify": "^5.3.3",
56
- "fastify-type-provider-zod": "^6.0.0",
57
- "fishery": "^2.4.0",
58
- "@shipfox/biome": "1.8.2",
59
- "@shipfox/depcruise": "1.0.2",
60
- "@shipfox/swc": "1.2.6",
61
- "@shipfox/ts-config": "1.3.8",
62
- "@shipfox/typescript": "1.1.7",
63
- "@shipfox/vitest": "1.2.3"
64
- },
65
48
  "scripts": {
66
49
  "build": "shipfox-swc && shipfox-temporal-bundle dist/temporal/workflows/index.js",
67
50
  "check": "shipfox-biome-check",
@@ -1,3 +1,4 @@
1
+ import {reportError} from '@shipfox/node-error-monitoring';
1
2
  import {logger} from '@shipfox/node-opentelemetry';
2
3
  import {advanceCronSchedule, claimDueCronSchedules, selectDbNow} from '#db/cron-schedules.js';
3
4
  import {db} from '#db/db.js';
@@ -84,6 +85,11 @@ export async function drainDueCronSchedules(
84
85
  {err: error, subscriptionId: schedule.subscriptionId},
85
86
  'cron drain: transient fire failure; schedule left due for retry',
86
87
  );
88
+ reportError(error, {
89
+ boundary: 'triggers.maintenance',
90
+ operation: 'drain-cron-schedules',
91
+ extra: {subscriptionId: schedule.subscriptionId},
92
+ });
87
93
  }
88
94
  params.onScheduleProcessed?.();
89
95
  }
@@ -1,3 +1,4 @@
1
+ import {reportError} from '@shipfox/node-error-monitoring';
1
2
  import {logger} from '@shipfox/node-opentelemetry';
2
3
  import type {JobListenerSubscription} from '#core/entities/job-listener-subscription.js';
3
4
  import type {TriggerEventOrigin} from '#core/entities/received-event.js';
@@ -140,6 +141,11 @@ async function safe<T>(
140
141
  {err: error, label, eventRef, ...(subscriptionId ? {subscriptionId} : {})},
141
142
  'trigger history write failed; ignored (best-effort)',
142
143
  );
144
+ reportError(error, {
145
+ boundary: 'triggers.history',
146
+ operation: label,
147
+ extra: {eventRef, subscriptionId},
148
+ });
143
149
  return undefined;
144
150
  }
145
151
  }
@@ -131,3 +131,12 @@ export async function findMatchingJobListenerSubscriptions(
131
131
  );
132
132
  return rows.map(toJobListenerSubscription);
133
133
  }
134
+
135
+ export async function hasJobListenerSubscriptions(jobId: string): Promise<boolean> {
136
+ const [subscription] = await db()
137
+ .select({id: jobListenerSubscriptions.id})
138
+ .from(jobListenerSubscriptions)
139
+ .where(eq(jobListenerSubscriptions.jobId, jobId))
140
+ .limit(1);
141
+ return subscription !== undefined;
142
+ }
package/src/index.ts CHANGED
@@ -18,6 +18,7 @@ import type {WorkflowsModuleClient} from '@shipfox/api-workflows-dto/inter-modul
18
18
  import {type ShipfoxModule, subscriberFactory} from '@shipfox/node-module';
19
19
  import {db, migrationsPath, triggersOutbox} from '#db/index.js';
20
20
  import {registerTriggersServiceMetrics} from '#metrics/index.js';
21
+ import {triggersE2eRoutes} from '#presentation/e2e-routes.js';
21
22
  import {createTriggerRoutes} from '#presentation/index.js';
22
23
  import {
23
24
  createOnIntegrationEventReceived,
@@ -76,6 +77,7 @@ export function createTriggersModule({workflows}: CreateTriggersModuleOptions):
76
77
  name: 'triggers',
77
78
  database: {db, migrationsPath},
78
79
  routes: createTriggerRoutes(workflows),
80
+ e2eRoutes: [triggersE2eRoutes],
79
81
  metrics: registerTriggersServiceMetrics,
80
82
  publishers: [{name: 'triggers', table: triggersOutbox, db}],
81
83
  subscribers: [
@@ -0,0 +1,44 @@
1
+ import {closeApp, createApp} from '@shipfox/node-fastify';
2
+ import {projectJobListenerSubscriptions} from '#db/job-listener-subscriptions.js';
3
+ import {triggersE2eRoutes} from './e2e-routes.js';
4
+
5
+ describe('triggers E2E routes', () => {
6
+ afterEach(async () => {
7
+ await closeApp();
8
+ });
9
+
10
+ test('reports a listener without projected subscriptions as not ready', async () => {
11
+ const jobId = crypto.randomUUID();
12
+ const app = await createApp({routes: [triggersE2eRoutes], swagger: false});
13
+
14
+ const response = await app.inject({
15
+ method: 'GET',
16
+ url: `/triggers/listeners/${jobId}/readiness`,
17
+ });
18
+
19
+ expect(response.statusCode).toBe(200);
20
+ expect(response.json()).toEqual({ready: false});
21
+ });
22
+
23
+ test('reports a listener with projected subscriptions as ready', async () => {
24
+ const workspaceId = crypto.randomUUID();
25
+ const workflowRunId = crypto.randomUUID();
26
+ const jobId = crypto.randomUUID();
27
+ await projectJobListenerSubscriptions({
28
+ workspaceId,
29
+ workflowRunId,
30
+ jobId,
31
+ on: [{source: 'listener-source', event: 'received'}],
32
+ until: null,
33
+ });
34
+ const app = await createApp({routes: [triggersE2eRoutes], swagger: false});
35
+
36
+ const response = await app.inject({
37
+ method: 'GET',
38
+ url: `/triggers/listeners/${jobId}/readiness`,
39
+ });
40
+
41
+ expect(response.statusCode).toBe(200);
42
+ expect(response.json()).toEqual({ready: true});
43
+ });
44
+ });
@@ -0,0 +1,24 @@
1
+ import {defineRoute, type RouteGroup} from '@shipfox/node-fastify';
2
+ import {z} from 'zod';
3
+ import {hasJobListenerSubscriptions} from '#db/job-listener-subscriptions.js';
4
+
5
+ const listenerReadinessParamsSchema = z.object({jobId: z.string().uuid()});
6
+ const listenerReadinessResponseSchema = z.object({ready: z.boolean()});
7
+
8
+ const listenerReadinessRoute = defineRoute({
9
+ method: 'GET',
10
+ path: '/listeners/:jobId/readiness',
11
+ description: 'Report whether trigger subscriptions for a listener job are ready in E2E tests.',
12
+ schema: {
13
+ params: listenerReadinessParamsSchema,
14
+ response: {200: listenerReadinessResponseSchema},
15
+ },
16
+ handler: async (request) => ({
17
+ ready: await hasJobListenerSubscriptions(request.params.jobId),
18
+ }),
19
+ });
20
+
21
+ export const triggersE2eRoutes: RouteGroup = {
22
+ prefix: '/triggers',
23
+ routes: [listenerReadinessRoute],
24
+ };