@shipfox/api-workflows 9.2.0 → 9.3.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.
Files changed (39) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/CHANGELOG.md +21 -0
  3. package/dist/core/errors.d.ts +12 -0
  4. package/dist/core/errors.d.ts.map +1 -1
  5. package/dist/core/errors.js +18 -0
  6. package/dist/core/errors.js.map +1 -1
  7. package/dist/core/workspace-admission.d.ts +3 -0
  8. package/dist/core/workspace-admission.d.ts.map +1 -0
  9. package/dist/core/workspace-admission.js +32 -0
  10. package/dist/core/workspace-admission.js.map +1 -0
  11. package/dist/index.d.ts +3 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +5 -3
  14. package/dist/index.js.map +1 -1
  15. package/dist/presentation/inter-module.d.ts +2 -0
  16. package/dist/presentation/inter-module.d.ts.map +1 -1
  17. package/dist/presentation/inter-module.js +40 -5
  18. package/dist/presentation/inter-module.js.map +1 -1
  19. package/dist/presentation/routes/index.d.ts +4 -1
  20. package/dist/presentation/routes/index.d.ts.map +1 -1
  21. package/dist/presentation/routes/index.js +1 -1
  22. package/dist/presentation/routes/index.js.map +1 -1
  23. package/dist/presentation/routes/rerun-run.d.ts +2 -1
  24. package/dist/presentation/routes/rerun-run.d.ts.map +1 -1
  25. package/dist/presentation/routes/rerun-run.js +22 -2
  26. package/dist/presentation/routes/rerun-run.js.map +1 -1
  27. package/dist/tsconfig.test.tsbuildinfo +1 -1
  28. package/package.json +9 -8
  29. package/src/core/errors.ts +21 -0
  30. package/src/core/workspace-admission.test.ts +64 -0
  31. package/src/core/workspace-admission.ts +43 -0
  32. package/src/index.ts +5 -0
  33. package/src/presentation/inter-module.test.ts +198 -1
  34. package/src/presentation/inter-module.ts +55 -3
  35. package/src/presentation/routes/index.test.ts +1 -0
  36. package/src/presentation/routes/index.ts +6 -2
  37. package/src/presentation/routes/rerun-run.test.ts +58 -1
  38. package/src/presentation/routes/rerun-run.ts +34 -2
  39. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,5 +1,10 @@
1
1
  import {buildUserContext, setUserContext} from '@shipfox/api-auth-context';
2
2
  import type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';
3
+ import {
4
+ type WorkspacesInterModuleClient,
5
+ workspacesInterModuleContract,
6
+ } from '@shipfox/api-workspaces-dto/inter-module';
7
+ import {createInterModuleKnownError} from '@shipfox/inter-module';
3
8
  import {ClientError} from '@shipfox/node-fastify';
4
9
  import type {FastifyInstance} from 'fastify';
5
10
  import Fastify from 'fastify';
@@ -20,6 +25,8 @@ const projects = {
20
25
  getProjectById,
21
26
  requireProjectForWorkspace: vi.fn(),
22
27
  } as unknown as ProjectsModuleClient;
28
+ const getWorkspaceOperatingState = vi.fn();
29
+ const workspaces = {getWorkspaceOperatingState} as unknown as WorkspacesInterModuleClient;
23
30
 
24
31
  describe('POST /api/workflows/runs/:id/rerun', () => {
25
32
  let app: FastifyInstance;
@@ -41,7 +48,7 @@ describe('POST /api/workflows/runs/:id/rerun', () => {
41
48
  );
42
49
  done();
43
50
  });
44
- app.post('/api/workflows/runs/:id/rerun', rerunRunRoute(projects));
51
+ app.post('/api/workflows/runs/:id/rerun', rerunRunRoute(projects, workspaces));
45
52
  await app.ready();
46
53
  });
47
54
 
@@ -60,6 +67,7 @@ describe('POST /api/workflows/runs/:id/rerun', () => {
60
67
  },
61
68
  }),
62
69
  );
70
+ getWorkspaceOperatingState.mockResolvedValue({status: 'active'});
63
71
  });
64
72
 
65
73
  async function createTerminalRun(status: 'succeeded' | 'failed' = 'failed') {
@@ -135,6 +143,55 @@ describe('POST /api/workflows/runs/:id/rerun', () => {
135
143
  expect(res.json().code).toBe('no-failed-jobs');
136
144
  });
137
145
 
146
+ test('returns workspace-suspended before creating a new attempt', async () => {
147
+ const source = await createTerminalRun('failed');
148
+ getWorkspaceOperatingState.mockResolvedValueOnce({status: 'suspended'});
149
+
150
+ const res = await app.inject({
151
+ method: 'POST',
152
+ url: `/api/workflows/runs/${source.id}/rerun`,
153
+ payload: {mode: 'all'},
154
+ });
155
+
156
+ expect(res.statusCode).toBe(409);
157
+ expect(res.json().code).toBe('workspace-suspended');
158
+ expect(getWorkspaceOperatingState).toHaveBeenCalledWith({workspaceId});
159
+ });
160
+
161
+ test('returns workspace-deleted before creating a new attempt', async () => {
162
+ const source = await createTerminalRun('failed');
163
+ getWorkspaceOperatingState.mockResolvedValueOnce({status: 'deleted'});
164
+
165
+ const res = await app.inject({
166
+ method: 'POST',
167
+ url: `/api/workflows/runs/${source.id}/rerun`,
168
+ payload: {mode: 'all'},
169
+ });
170
+
171
+ expect(res.statusCode).toBe(404);
172
+ expect(res.json().code).toBe('workspace-deleted');
173
+ });
174
+
175
+ test('returns workspace-not-found before creating a new attempt', async () => {
176
+ const source = await createTerminalRun('failed');
177
+ getWorkspaceOperatingState.mockRejectedValueOnce(
178
+ createInterModuleKnownError(
179
+ workspacesInterModuleContract.methods.getWorkspaceOperatingState,
180
+ 'workspace-not-found',
181
+ {workspaceId},
182
+ ),
183
+ );
184
+
185
+ const res = await app.inject({
186
+ method: 'POST',
187
+ url: `/api/workflows/runs/${source.id}/rerun`,
188
+ payload: {mode: 'all'},
189
+ });
190
+
191
+ expect(res.statusCode).toBe(404);
192
+ expect(res.json().code).toBe('workspace-not-found');
193
+ });
194
+
138
195
  test('returns 409 when the source run is not terminal', async () => {
139
196
  const source = await createWorkflowRun({
140
197
  workspaceId,
@@ -1,14 +1,26 @@
1
1
  import {requireUserContext} from '@shipfox/api-auth-context';
2
2
  import type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';
3
3
  import {rerunWorkflowRunBodySchema, workflowRunResponseSchema} from '@shipfox/api-workflows-dto';
4
+ import type {WorkspacesInterModuleClient} from '@shipfox/api-workspaces-dto/inter-module';
4
5
  import {ClientError, defineRoute} from '@shipfox/node-fastify';
5
6
  import {z} from 'zod';
6
- import {NoFailedJobsError, RunNotTerminalError, SourceRunNotFoundError} from '#core/errors.js';
7
+ import {
8
+ NoFailedJobsError,
9
+ RunNotTerminalError,
10
+ SourceRunNotFoundError,
11
+ WorkspaceDeletedError,
12
+ WorkspaceNotFoundError,
13
+ WorkspaceSuspendedError,
14
+ } from '#core/errors.js';
15
+ import {assertWorkspaceAdmitsNewJobs} from '#core/workspace-admission.js';
7
16
  import {createRerunWorkflowRun} from '#db/index.js';
8
17
  import {toRunDto} from '#presentation/dto/index.js';
9
18
  import {requireAccessibleRun} from './require-accessible-run.js';
10
19
 
11
- export function rerunRunRoute(projects: ProjectsModuleClient) {
20
+ export function rerunRunRoute(
21
+ projects: ProjectsModuleClient,
22
+ workspaces: WorkspacesInterModuleClient,
23
+ ) {
12
24
  return defineRoute({
13
25
  method: 'POST',
14
26
  path: '/:id/rerun',
@@ -32,12 +44,32 @@ export function rerunRunRoute(projects: ProjectsModuleClient) {
32
44
  if (error instanceof NoFailedJobsError) {
33
45
  throw new ClientError('Run has no failed jobs', 'no-failed-jobs', {status: 409});
34
46
  }
47
+ if (error instanceof WorkspaceSuspendedError) {
48
+ throw new ClientError('Workspace is suspended', 'workspace-suspended', {
49
+ status: 409,
50
+ cause: error,
51
+ });
52
+ }
53
+ if (error instanceof WorkspaceDeletedError) {
54
+ throw new ClientError('Workspace is deleted', 'workspace-deleted', {
55
+ status: 404,
56
+ cause: error,
57
+ });
58
+ }
59
+ if (error instanceof WorkspaceNotFoundError) {
60
+ throw new ClientError('Workspace not found', 'workspace-not-found', {
61
+ status: 404,
62
+ cause: error,
63
+ });
64
+ }
35
65
  throw error;
36
66
  },
37
67
  handler: async (request) => {
38
68
  const {id} = request.params;
39
69
  const sourceRun = await requireAccessibleRun({request, id, projects});
40
70
 
71
+ await assertWorkspaceAdmitsNewJobs(workspaces, sourceRun.workspaceId);
72
+
41
73
  const actor = requireUserContext(request);
42
74
  const run = await createRerunWorkflowRun({
43
75
  workflowRunId: sourceRun.id,