@shipfox/api-server 2.0.0 → 4.0.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
@@ -2,7 +2,7 @@
2
2
  "name": "@shipfox/api-server",
3
3
  "license": "MIT",
4
4
  "private": false,
5
- "version": "2.0.0",
5
+ "version": "4.0.0",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/ShipfoxHQ/shipfox.git",
@@ -41,32 +41,32 @@
41
41
  }
42
42
  },
43
43
  "dependencies": {
44
- "@shipfox/api-auth": "2.0.0",
45
- "@shipfox/api-agent": "2.0.0",
46
- "@shipfox/api-dispatcher": "2.0.0",
47
- "@shipfox/annotations": "2.0.0",
48
- "@shipfox/api-integration-core": "2.0.0",
49
- "@shipfox/api-definitions": "2.0.0",
50
- "@shipfox/api-logs": "2.0.0",
51
- "@shipfox/api-projects": "2.0.0",
52
- "@shipfox/api-workspaces": "2.0.0",
53
- "@shipfox/api-triggers": "2.0.0",
44
+ "@shipfox/annotations": "4.0.0",
45
+ "@shipfox/api-agent": "4.0.0",
46
+ "@shipfox/api-auth": "4.0.0",
47
+ "@shipfox/api-definitions": "4.0.0",
48
+ "@shipfox/api-dispatcher": "4.0.0",
49
+ "@shipfox/api-integration-core": "4.0.0",
50
+ "@shipfox/api-logs": "4.0.0",
51
+ "@shipfox/api-projects": "4.0.0",
52
+ "@shipfox/api-runners": "4.0.0",
53
+ "@shipfox/api-secrets": "4.0.0",
54
+ "@shipfox/api-triggers": "4.0.0",
55
+ "@shipfox/api-workflows": "4.0.0",
56
+ "@shipfox/api-workspaces": "4.0.0",
54
57
  "@shipfox/config": "1.2.1",
55
- "@shipfox/node-fastify": "0.2.1",
56
58
  "@shipfox/node-error-monitoring": "0.1.2",
57
- "@shipfox/api-workflows": "2.0.0",
58
- "@shipfox/node-module": "0.2.0",
59
- "@shipfox/api-secrets": "2.0.0",
60
- "@shipfox/node-postgres": "0.4.1",
61
- "@shipfox/api-runners": "2.0.0",
62
- "@shipfox/node-opentelemetry": "0.5.0"
59
+ "@shipfox/node-fastify": "0.2.2",
60
+ "@shipfox/node-module": "0.3.1",
61
+ "@shipfox/node-opentelemetry": "0.5.1",
62
+ "@shipfox/node-postgres": "0.4.1"
63
63
  },
64
64
  "devDependencies": {
65
- "@shipfox/ts-config": "1.3.8",
66
65
  "@shipfox/biome": "1.8.1",
67
66
  "@shipfox/swc": "1.2.5",
68
- "@shipfox/vitest": "1.2.2",
69
- "@shipfox/typescript": "1.1.6"
67
+ "@shipfox/ts-config": "1.3.8",
68
+ "@shipfox/typescript": "1.1.6",
69
+ "@shipfox/vitest": "1.2.2"
70
70
  },
71
71
  "scripts": {
72
72
  "build": "shipfox-swc",
@@ -96,7 +96,7 @@ describe('defaultModules', () => {
96
96
  ]);
97
97
  });
98
98
 
99
- it('uses the leased-step loader and namespaced Linear secrets', async () => {
99
+ it('uses the leased-step loader and namespaced provider secrets', async () => {
100
100
  await defaultModules();
101
101
 
102
102
  expect(mocks.createIntegrationsContext).toHaveBeenCalledWith({
@@ -107,6 +107,11 @@ describe('defaultModules', () => {
107
107
  getSecret: expect.any(Function),
108
108
  setSecrets: expect.any(Function),
109
109
  },
110
+ slack: {
111
+ deleteSecrets: expect.any(Function),
112
+ getSecret: expect.any(Function),
113
+ setSecrets: expect.any(Function),
114
+ },
110
115
  },
111
116
  agentTools: {loadLeasedAgentStep: mocks.loadRunningLeasedStep},
112
117
  });
@@ -118,11 +123,19 @@ describe('defaultModules', () => {
118
123
  getSecret: (params: {namespace: string}) => unknown;
119
124
  setSecrets: (params: {namespace: string}) => unknown;
120
125
  };
126
+ slack: {
127
+ deleteSecrets: (params: {namespace: string}) => unknown;
128
+ getSecret: (params: {namespace: string}) => unknown;
129
+ setSecrets: (params: {namespace: string}) => unknown;
130
+ };
121
131
  };
122
132
  };
123
133
  integrationsOptions.secrets.linear.getSecret({namespace: 'workspace'});
124
134
  integrationsOptions.secrets.linear.setSecrets({namespace: 'workspace'});
125
135
  integrationsOptions.secrets.linear.deleteSecrets({namespace: 'workspace'});
136
+ integrationsOptions.secrets.slack.getSecret({namespace: 'workspace'});
137
+ integrationsOptions.secrets.slack.setSecrets({namespace: 'workspace'});
138
+ integrationsOptions.secrets.slack.deleteSecrets({namespace: 'workspace'});
126
139
 
127
140
  expect(mocks.getSecret).toHaveBeenCalledWith({
128
141
  namespace: 'system/integrations/linear/workspace',
@@ -133,5 +146,14 @@ describe('defaultModules', () => {
133
146
  expect(mocks.deleteSecrets).toHaveBeenCalledWith({
134
147
  namespace: 'system/integrations/linear/workspace',
135
148
  });
149
+ expect(mocks.getSecret).toHaveBeenCalledWith({
150
+ namespace: 'system/integrations/slack/workspace',
151
+ });
152
+ expect(mocks.setSecrets).toHaveBeenCalledWith({
153
+ namespace: 'system/integrations/slack/workspace',
154
+ });
155
+ expect(mocks.deleteSecrets).toHaveBeenCalledWith({
156
+ namespace: 'system/integrations/slack/workspace',
157
+ });
136
158
  });
137
159
  });
package/src/modules.ts CHANGED
@@ -45,6 +45,23 @@ export async function defaultModules(): Promise<ShipfoxModule[]> {
45
45
  namespace: `system/integrations/linear/${params.namespace}`,
46
46
  }),
47
47
  },
48
+ slack: {
49
+ getSecret: (params) =>
50
+ getSecret({
51
+ ...params,
52
+ namespace: `system/integrations/slack/${params.namespace}`,
53
+ }),
54
+ setSecrets: (params) =>
55
+ setSecrets({
56
+ ...params,
57
+ namespace: `system/integrations/slack/${params.namespace}`,
58
+ }),
59
+ deleteSecrets: (params) =>
60
+ deleteSecrets({
61
+ ...params,
62
+ namespace: `system/integrations/slack/${params.namespace}`,
63
+ }),
64
+ },
48
65
  },
49
66
  agentTools: {loadLeasedAgentStep: loadRunningLeasedStep},
50
67
  });
@@ -17,6 +17,7 @@ const mocks = vi.hoisted(() => {
17
17
  createE2eAdminAuthMethod: vi.fn(),
18
18
  createE2eRouteGroup: vi.fn(),
19
19
  createPostgresClient: vi.fn(),
20
+ aggregateLoginMethods: vi.fn(),
20
21
  initializeModules: vi.fn(),
21
22
  listen: vi.fn(),
22
23
  logger: {error: vi.fn(), info: vi.fn()},
@@ -47,6 +48,7 @@ vi.mock('@shipfox/node-fastify', () => ({
47
48
  listen: mocks.listen,
48
49
  }));
49
50
  vi.mock('@shipfox/node-module', () => ({
51
+ aggregateLoginMethods: mocks.aggregateLoginMethods,
50
52
  initializeModules: mocks.initializeModules,
51
53
  registerModuleMetrics: mocks.registerModuleMetrics,
52
54
  resetPublishers: mocks.resetPublishers,
@@ -73,7 +75,7 @@ vi.mock('./e2e.js', () => ({
73
75
  }));
74
76
 
75
77
  function module(): ShipfoxModule {
76
- return {name: 'test'};
78
+ return {name: 'test', loginMethods: [{id: 'test-login'}]};
77
79
  }
78
80
 
79
81
  function worker(taskQueue = 'runtime-queue'): ModuleWorker {
@@ -101,6 +103,7 @@ function resetMocks(): void {
101
103
  mocks.createE2eAdminAuthMethod.mockReset();
102
104
  mocks.createE2eRouteGroup.mockReset();
103
105
  mocks.createPostgresClient.mockReset();
106
+ mocks.aggregateLoginMethods.mockReset();
104
107
  mocks.initializeModules.mockReset();
105
108
  mocks.listen.mockReset();
106
109
  mocks.logger.error.mockReset();
@@ -122,6 +125,7 @@ function resetMocks(): void {
122
125
  mocks.closeErrorMonitoring.mockResolvedValue(true);
123
126
  mocks.closePostgresClient.mockResolvedValue(undefined);
124
127
  mocks.createApp.mockResolvedValue({});
128
+ mocks.aggregateLoginMethods.mockReturnValue([{id: 'test-login'}]);
125
129
  mocks.createE2eRouteGroup.mockReturnValue([]);
126
130
  mocks.initializeModules.mockResolvedValue({
127
131
  auth: [],
@@ -166,6 +170,7 @@ describe('createServer', () => {
166
170
  await createTestServer({modules});
167
171
 
168
172
  expect(mocks.startServiceMetrics).toHaveBeenCalledWith({serviceName: 'api'});
173
+ expect(mocks.aggregateLoginMethods).toHaveBeenCalledWith({modules});
169
174
  expect(mocks.createPostgresClient).toHaveBeenCalledOnce();
170
175
  expect(mocks.initializeModules).toHaveBeenCalledWith({modules});
171
176
  expect(mocks.registerModuleMetrics).toHaveBeenCalledWith({modules});
@@ -177,6 +182,28 @@ describe('createServer', () => {
177
182
  });
178
183
  });
179
184
 
185
+ it('fails before startup side effects when no login method is configured', async () => {
186
+ const failure = new Error('No login methods are configured');
187
+ mocks.aggregateLoginMethods.mockImplementationOnce(() => {
188
+ throw failure;
189
+ });
190
+
191
+ const result = createTestServer({modules: [module()]});
192
+
193
+ await expect(result).rejects.toBe(failure);
194
+ expect(mocks.startServiceMetrics).not.toHaveBeenCalled();
195
+ expect(mocks.createPostgresClient).not.toHaveBeenCalled();
196
+ });
197
+
198
+ it('constructs and starts when a module contributes a login method', async () => {
199
+ const server = await createTestServer({modules: [module()]});
200
+
201
+ await server.start();
202
+
203
+ expect(mocks.startModuleWorkers).toHaveBeenCalledOnce();
204
+ expect(mocks.listen).toHaveBeenCalledOnce();
205
+ });
206
+
180
207
  it('starts module workers before listening for HTTP requests', async () => {
181
208
  const server = await createTestServer({modules: [module()]});
182
209
  let resolveWorkers: ((handle: ModuleWorkersHandle) => void) | undefined;
package/src/server.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import {captureException, closeErrorMonitoring} from '@shipfox/node-error-monitoring';
2
2
  import {closeApp, createApp, listen} from '@shipfox/node-fastify';
3
3
  import {
4
+ aggregateLoginMethods,
4
5
  initializeModules,
5
6
  type ModuleWorker,
6
7
  type ModuleWorkersHandle,
@@ -43,6 +44,7 @@ export async function createServer(options: CreateServerOptions): Promise<Server
43
44
  hasActiveServer = true;
44
45
 
45
46
  try {
47
+ aggregateLoginMethods({modules: options.modules});
46
48
  startServiceMetrics({serviceName: 'api'});
47
49
  createPostgresClient();
48
50