@opengeni/api-router 2.6.4 → 2.10.0-canary.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/dist/access-grant-rls.d.ts +3 -0
- package/dist/app.d.ts +2 -1
- package/dist/app.js +3 -1
- package/dist/auth/organization-user-setup.d.ts +23 -0
- package/dist/{chunk-XTLI3CBH.js → chunk-XXH7DW34.js} +6866 -3554
- package/dist/chunk-XXH7DW34.js.map +1 -0
- package/dist/codemode.d.ts +6 -0
- package/dist/github-access.d.ts +25 -2
- package/dist/http/request-source.d.ts +14 -0
- package/dist/index.js +19 -3
- package/dist/index.js.map +1 -1
- package/dist/integrations/oauth-client.d.ts +2 -11
- package/dist/integrations/personal-github-repositories.d.ts +41 -2
- package/dist/integrations/slack-interactions.d.ts +1 -1
- package/dist/mcp/server.d.ts +158 -1
- package/dist/mcp/session-view.d.ts +56 -2
- package/dist/mcp/session-wait.d.ts +1 -1
- package/dist/mcp-oauth.d.ts +19 -0
- package/dist/model-catalog.d.ts +5 -31
- package/dist/routes/codex.d.ts +27 -2
- package/dist/routes/github.d.ts +2 -0
- package/dist/routes/organization-model-providers.d.ts +3 -0
- package/dist/routes/workspace-artifacts.d.ts +2 -1
- package/dist/work-discovery-observability.d.ts +1 -1
- package/dist/workspace-artifact-content.d.ts +28 -0
- package/dist/workspace-artifact-provenance.d.ts +7 -0
- package/dist/workspace-deletion.d.ts +6 -0
- package/dist/workspace-tool-gateway-observability.d.ts +17 -0
- package/dist/workspace-tool-gateway.d.ts +118 -0
- package/package.json +19 -18
- package/src/access-grant-rls.ts +40 -0
- package/src/app.ts +296 -53
- package/src/auth/organization-user-setup.ts +95 -5
- package/src/codemode.ts +33 -4
- package/src/github-access.ts +117 -1
- package/src/http/auth.ts +11 -0
- package/src/http/request-source.ts +37 -0
- package/src/http/sse.ts +15 -7
- package/src/index.ts +18 -2
- package/src/integrations/oauth-client.ts +168 -203
- package/src/integrations/personal-github-repositories.ts +238 -9
- package/src/integrations/slack-app-home.ts +2 -2
- package/src/integrations/slack-interactions.ts +77 -37
- package/src/mcp/company-brain-governed-writes.ts +2 -2
- package/src/mcp/company-profile-agent-admin.ts +1 -1
- package/src/mcp/remember.ts +1 -1
- package/src/mcp/server.ts +504 -133
- package/src/mcp/session-view.ts +20 -1
- package/src/mcp/session-wait.ts +3 -0
- package/src/mcp-oauth.ts +539 -0
- package/src/model-catalog.ts +45 -337
- package/src/routes/automations.ts +178 -19
- package/src/routes/codex.ts +242 -73
- package/src/routes/connections.ts +271 -16
- package/src/routes/documents.ts +67 -19
- package/src/routes/files.ts +54 -6
- package/src/routes/github.ts +116 -15
- package/src/routes/organization-memberships.ts +59 -16
- package/src/routes/organization-model-providers.ts +231 -0
- package/src/routes/packs.ts +1 -0
- package/src/routes/personal-github.ts +39 -0
- package/src/routes/pr-review.ts +72 -4
- package/src/routes/scheduled-tasks.ts +40 -13
- package/src/routes/sessions.ts +153 -65
- package/src/routes/supergrok.ts +3 -2
- package/src/routes/workspace-artifacts.ts +176 -67
- package/src/routes/workspaces.ts +405 -62
- package/src/sandbox/channel-a.ts +17 -4
- package/src/work-discovery-observability.ts +3 -3
- package/src/workspace-artifact-content.ts +163 -0
- package/src/workspace-artifact-provenance.ts +104 -0
- package/src/workspace-deletion.ts +64 -0
- package/src/workspace-tool-gateway-observability.ts +100 -0
- package/src/workspace-tool-gateway.ts +691 -0
- package/dist/chunk-XTLI3CBH.js.map +0 -1
|
@@ -3,22 +3,32 @@ import {
|
|
|
3
3
|
CreateWorkspaceArtifactRequest,
|
|
4
4
|
PublishWorkspaceArtifactVersionRequest,
|
|
5
5
|
RollbackWorkspaceArtifactRequest,
|
|
6
|
+
SetWorkspaceArtifactStatusRequest,
|
|
6
7
|
WorkspaceArtifactContentResponse,
|
|
7
8
|
WorkspaceArtifactDetailResponse,
|
|
8
9
|
WorkspaceArtifactListQuery,
|
|
9
10
|
WorkspaceArtifactListResponse,
|
|
10
11
|
WorkspaceArtifactMutationResponse,
|
|
11
12
|
normalizeWorkspaceArtifactSlug,
|
|
13
|
+
type AccessGrant,
|
|
12
14
|
} from "@opengeni/contracts";
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
+
import {
|
|
16
|
+
requireAccessGrant,
|
|
17
|
+
requireAccessGrantAuthorization,
|
|
18
|
+
requireSessionAuthorization,
|
|
19
|
+
SessionAuthorizationDeniedError,
|
|
20
|
+
SessionAuthorizationUnavailableError,
|
|
21
|
+
type ApiRouteDeps,
|
|
22
|
+
} from "@opengeni/core";
|
|
15
23
|
import {
|
|
16
24
|
createWorkspaceArtifact,
|
|
17
25
|
getWorkspaceArtifact,
|
|
18
26
|
getWorkspaceArtifactContentRef,
|
|
19
27
|
listWorkspaceArtifacts,
|
|
28
|
+
nestedPostgresSqlState,
|
|
20
29
|
publishWorkspaceArtifactVersion,
|
|
21
30
|
rollbackWorkspaceArtifact,
|
|
31
|
+
setWorkspaceArtifactStatus,
|
|
22
32
|
WorkspaceArtifactConflictError,
|
|
23
33
|
WorkspaceArtifactNotFoundError,
|
|
24
34
|
WorkspaceArtifactOperationError,
|
|
@@ -26,10 +36,17 @@ import {
|
|
|
26
36
|
import type { Context, Hono } from "hono";
|
|
27
37
|
import { HTTPException } from "hono/http-exception";
|
|
28
38
|
import { z } from "zod";
|
|
39
|
+
import {
|
|
40
|
+
prepareWorkspaceArtifactContent,
|
|
41
|
+
readWorkspaceArtifactContent,
|
|
42
|
+
} from "../workspace-artifact-content";
|
|
43
|
+
import {
|
|
44
|
+
projectWorkspaceArtifactDetailProvenance,
|
|
45
|
+
projectWorkspaceArtifactMutationProvenance,
|
|
46
|
+
redactWorkspaceArtifactListProvenance,
|
|
47
|
+
} from "../workspace-artifact-provenance";
|
|
29
48
|
|
|
30
49
|
const ArtifactId = z.string().uuid();
|
|
31
|
-
const encoder = new TextEncoder();
|
|
32
|
-
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
33
50
|
|
|
34
51
|
async function body<S extends z.ZodType>(context: Context, schema: S): Promise<z.infer<S>> {
|
|
35
52
|
const parsed = schema.safeParse(await context.req.json().catch(() => null));
|
|
@@ -43,7 +60,7 @@ function artifactId(context: Context): string {
|
|
|
43
60
|
return parsed.data;
|
|
44
61
|
}
|
|
45
62
|
|
|
46
|
-
function
|
|
63
|
+
export function workspaceArtifactErrorResponse(context: Context, error: unknown): Response {
|
|
47
64
|
if (error instanceof WorkspaceArtifactNotFoundError) {
|
|
48
65
|
return context.json({ code: "WORKSPACE_ARTIFACT_NOT_FOUND", message: error.message }, 404);
|
|
49
66
|
}
|
|
@@ -63,41 +80,28 @@ function errorResponse(context: Context, error: unknown): Response {
|
|
|
63
80
|
422,
|
|
64
81
|
);
|
|
65
82
|
}
|
|
66
|
-
if (
|
|
83
|
+
if (nestedPostgresSqlState(error) === "23505") {
|
|
67
84
|
return context.json(
|
|
68
|
-
{
|
|
85
|
+
{
|
|
86
|
+
code: "WORKSPACE_ARTIFACT_CONFLICT",
|
|
87
|
+
message: "Artifact slug or operation already exists",
|
|
88
|
+
},
|
|
69
89
|
409,
|
|
70
90
|
);
|
|
71
91
|
}
|
|
72
92
|
throw error;
|
|
73
93
|
}
|
|
74
94
|
|
|
75
|
-
function
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
contentSha256: sha256,
|
|
81
|
-
sizeBytes: bytes.byteLength,
|
|
82
|
-
contentKey: `workspaces/${workspaceId}/workspace-artifacts/blobs/${sha256}.html`,
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function prepareHtml(deps: ApiRouteDeps, workspaceId: string, html: string) {
|
|
95
|
+
function prepareContent(
|
|
96
|
+
deps: ApiRouteDeps,
|
|
97
|
+
workspaceId: string,
|
|
98
|
+
input: Parameters<typeof prepareWorkspaceArtifactContent>[2],
|
|
99
|
+
) {
|
|
87
100
|
if (!deps.objectStorage)
|
|
88
|
-
throw new HTTPException(503, {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
persistContent: async () => {
|
|
93
|
-
await deps.objectStorage!.putObject({
|
|
94
|
-
key: content.contentKey,
|
|
95
|
-
contentType: "text/html; charset=utf-8",
|
|
96
|
-
body: content.bytes,
|
|
97
|
-
sha256: content.contentSha256,
|
|
98
|
-
});
|
|
99
|
-
},
|
|
100
|
-
};
|
|
101
|
+
throw new HTTPException(503, {
|
|
102
|
+
message: "Object storage is not configured",
|
|
103
|
+
});
|
|
104
|
+
return prepareWorkspaceArtifactContent(deps.objectStorage, workspaceId, input);
|
|
101
105
|
}
|
|
102
106
|
|
|
103
107
|
function provenance(subjectId: string, idempotencyKey: string) {
|
|
@@ -112,6 +116,41 @@ function provenance(subjectId: string, idempotencyKey: string) {
|
|
|
112
116
|
};
|
|
113
117
|
}
|
|
114
118
|
|
|
119
|
+
async function canReadProvenanceSession(
|
|
120
|
+
deps: ApiRouteDeps,
|
|
121
|
+
grant: AccessGrant,
|
|
122
|
+
sessionId: string,
|
|
123
|
+
): Promise<boolean> {
|
|
124
|
+
try {
|
|
125
|
+
await requireSessionAuthorization(deps, grant, {
|
|
126
|
+
sessionId,
|
|
127
|
+
operation: "session.read",
|
|
128
|
+
surface: "http",
|
|
129
|
+
});
|
|
130
|
+
return true;
|
|
131
|
+
} catch (error) {
|
|
132
|
+
if (error instanceof SessionAuthorizationDeniedError) return false;
|
|
133
|
+
if (error instanceof SessionAuthorizationUnavailableError) {
|
|
134
|
+
throw new HTTPException(503, {
|
|
135
|
+
message: "Session authorization is unavailable",
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function mutationResponse(
|
|
143
|
+
deps: ApiRouteDeps,
|
|
144
|
+
grant: AccessGrant,
|
|
145
|
+
response: WorkspaceArtifactMutationResponse,
|
|
146
|
+
): Promise<WorkspaceArtifactMutationResponse> {
|
|
147
|
+
return WorkspaceArtifactMutationResponse.parse(
|
|
148
|
+
await projectWorkspaceArtifactMutationProvenance(response, (sessionId) =>
|
|
149
|
+
canReadProvenanceSession(deps, grant, sessionId),
|
|
150
|
+
),
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
115
154
|
export function registerWorkspaceArtifactRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
116
155
|
// `published-artifacts` deliberately avoids the existing `/artifacts/:id`
|
|
117
156
|
// retained-output API. The product route remains simply `/artifacts`.
|
|
@@ -123,38 +162,55 @@ export function registerWorkspaceArtifactRoutes(app: Hono, deps: ApiRouteDeps):
|
|
|
123
162
|
const query = WorkspaceArtifactListQuery.safeParse({
|
|
124
163
|
limit: context.req.query("limit"),
|
|
125
164
|
cursor: context.req.query("cursor"),
|
|
165
|
+
status: context.req.query("status"),
|
|
126
166
|
});
|
|
127
167
|
if (!query.success) throw new HTTPException(422, { message: "Invalid artifact list query" });
|
|
128
168
|
try {
|
|
129
169
|
return context.json(
|
|
130
170
|
WorkspaceArtifactListResponse.parse(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
171
|
+
redactWorkspaceArtifactListProvenance(
|
|
172
|
+
await listWorkspaceArtifacts(deps.db, workspaceId, {
|
|
173
|
+
limit: query.data.limit,
|
|
174
|
+
...(query.data.cursor ? { cursor: query.data.cursor } : {}),
|
|
175
|
+
...(query.data.status ? { status: query.data.status } : {}),
|
|
176
|
+
}),
|
|
177
|
+
),
|
|
135
178
|
),
|
|
136
179
|
);
|
|
137
180
|
} catch (error) {
|
|
138
|
-
return
|
|
181
|
+
return workspaceArtifactErrorResponse(context, error);
|
|
139
182
|
}
|
|
140
183
|
});
|
|
141
184
|
|
|
142
185
|
app.post(base, async (context) => {
|
|
143
186
|
const workspaceId = context.req.param("workspaceId");
|
|
144
|
-
const
|
|
187
|
+
const authorization = await requireAccessGrantAuthorization(
|
|
188
|
+
context,
|
|
189
|
+
deps,
|
|
190
|
+
workspaceId,
|
|
191
|
+
"artifacts:publish",
|
|
192
|
+
);
|
|
193
|
+
const grant = authorization.grant;
|
|
145
194
|
const request = await body(context, CreateWorkspaceArtifactRequest);
|
|
146
195
|
const id = crypto.randomUUID();
|
|
147
196
|
const slugBase = request.slug ?? (normalizeWorkspaceArtifactSlug(request.title) || "artifact");
|
|
148
197
|
const slug = request.slug ?? `${slugBase.slice(0, 87)}-${id.slice(0, 8)}`;
|
|
149
|
-
const content =
|
|
198
|
+
const content = prepareContent(deps, workspaceId, {
|
|
199
|
+
html: request.html,
|
|
200
|
+
...(request.source ? { source: request.source } : {}),
|
|
201
|
+
...(request.requestedTools ? { requestedTools: request.requestedTools } : {}),
|
|
202
|
+
});
|
|
150
203
|
try {
|
|
151
204
|
return context.json(
|
|
152
|
-
|
|
205
|
+
await mutationResponse(
|
|
206
|
+
deps,
|
|
207
|
+
grant,
|
|
153
208
|
await createWorkspaceArtifact(deps.db, {
|
|
154
209
|
accountId: grant.accountId,
|
|
155
210
|
workspaceId,
|
|
156
211
|
artifactId: id,
|
|
157
212
|
slug,
|
|
213
|
+
requestedSlug: request.slug ?? null,
|
|
158
214
|
title: request.title,
|
|
159
215
|
description: request.description ?? null,
|
|
160
216
|
...content,
|
|
@@ -164,21 +220,24 @@ export function registerWorkspaceArtifactRoutes(app: Hono, deps: ApiRouteDeps):
|
|
|
164
220
|
201,
|
|
165
221
|
);
|
|
166
222
|
} catch (error) {
|
|
167
|
-
return
|
|
223
|
+
return workspaceArtifactErrorResponse(context, error);
|
|
168
224
|
}
|
|
169
225
|
});
|
|
170
226
|
|
|
171
227
|
app.get(`${base}/:artifactId`, async (context) => {
|
|
172
228
|
const workspaceId = context.req.param("workspaceId");
|
|
173
|
-
await requireAccessGrant(context, deps, workspaceId, "artifacts:read");
|
|
229
|
+
const grant = await requireAccessGrant(context, deps, workspaceId, "artifacts:read");
|
|
174
230
|
try {
|
|
231
|
+
const detail = await getWorkspaceArtifact(deps.db, workspaceId, artifactId(context));
|
|
175
232
|
return context.json(
|
|
176
233
|
WorkspaceArtifactDetailResponse.parse(
|
|
177
|
-
await
|
|
234
|
+
await projectWorkspaceArtifactDetailProvenance(detail, (sessionId) =>
|
|
235
|
+
canReadProvenanceSession(deps, grant, sessionId),
|
|
236
|
+
),
|
|
178
237
|
),
|
|
179
238
|
);
|
|
180
239
|
} catch (error) {
|
|
181
|
-
return
|
|
240
|
+
return workspaceArtifactErrorResponse(context, error);
|
|
182
241
|
}
|
|
183
242
|
});
|
|
184
243
|
|
|
@@ -186,7 +245,9 @@ export function registerWorkspaceArtifactRoutes(app: Hono, deps: ApiRouteDeps):
|
|
|
186
245
|
const workspaceId = context.req.param("workspaceId");
|
|
187
246
|
await requireAccessGrant(context, deps, workspaceId, "artifacts:read");
|
|
188
247
|
if (!deps.objectStorage)
|
|
189
|
-
throw new HTTPException(503, {
|
|
248
|
+
throw new HTTPException(503, {
|
|
249
|
+
message: "Object storage is not configured",
|
|
250
|
+
});
|
|
190
251
|
const parsedVersion = context.req.query("versionId");
|
|
191
252
|
if (parsedVersion && !ArtifactId.safeParse(parsedVersion).success) {
|
|
192
253
|
throw new HTTPException(422, { message: "Invalid artifact version id" });
|
|
@@ -198,19 +259,14 @@ export function registerWorkspaceArtifactRoutes(app: Hono, deps: ApiRouteDeps):
|
|
|
198
259
|
artifactId(context),
|
|
199
260
|
parsedVersion,
|
|
200
261
|
);
|
|
201
|
-
|
|
202
|
-
deps.objectStorage!.getObjectBytes(ref.contentKey),
|
|
203
|
-
);
|
|
204
|
-
if (!object) throw new HTTPException(503, { message: "Artifact content is unavailable" });
|
|
205
|
-
const actualHash = createHash("sha256").update(object.bytes).digest("hex");
|
|
206
|
-
if (actualHash !== ref.version.contentSha256) {
|
|
207
|
-
throw new HTTPException(503, { message: "Artifact content failed integrity verification" });
|
|
208
|
-
}
|
|
209
|
-
let html: string;
|
|
262
|
+
let content: Awaited<ReturnType<typeof readWorkspaceArtifactContent>>;
|
|
210
263
|
try {
|
|
211
|
-
|
|
212
|
-
} catch {
|
|
213
|
-
throw new HTTPException(503, {
|
|
264
|
+
content = await readWorkspaceArtifactContent(deps.objectStorage, ref);
|
|
265
|
+
} catch (error) {
|
|
266
|
+
throw new HTTPException(503, {
|
|
267
|
+
message: error instanceof Error ? error.message : "Artifact content is unavailable",
|
|
268
|
+
cause: error,
|
|
269
|
+
});
|
|
214
270
|
}
|
|
215
271
|
return context.json(
|
|
216
272
|
WorkspaceArtifactContentResponse.parse({
|
|
@@ -218,23 +274,35 @@ export function registerWorkspaceArtifactRoutes(app: Hono, deps: ApiRouteDeps):
|
|
|
218
274
|
versionId: ref.version.id,
|
|
219
275
|
contentType: "text/html",
|
|
220
276
|
contentSha256: ref.version.contentSha256,
|
|
221
|
-
|
|
277
|
+
...content,
|
|
222
278
|
}),
|
|
223
279
|
);
|
|
224
280
|
} catch (error) {
|
|
225
|
-
return
|
|
281
|
+
return workspaceArtifactErrorResponse(context, error);
|
|
226
282
|
}
|
|
227
283
|
});
|
|
228
284
|
|
|
229
285
|
app.post(`${base}/:artifactId/versions`, async (context) => {
|
|
230
286
|
const workspaceId = context.req.param("workspaceId");
|
|
231
|
-
const
|
|
287
|
+
const authorization = await requireAccessGrantAuthorization(
|
|
288
|
+
context,
|
|
289
|
+
deps,
|
|
290
|
+
workspaceId,
|
|
291
|
+
"artifacts:publish",
|
|
292
|
+
);
|
|
293
|
+
const grant = authorization.grant;
|
|
232
294
|
const request = await body(context, PublishWorkspaceArtifactVersionRequest);
|
|
233
295
|
const id = artifactId(context);
|
|
234
|
-
const content =
|
|
296
|
+
const content = prepareContent(deps, workspaceId, {
|
|
297
|
+
html: request.html,
|
|
298
|
+
...(request.source ? { source: request.source } : {}),
|
|
299
|
+
...(request.requestedTools ? { requestedTools: request.requestedTools } : {}),
|
|
300
|
+
});
|
|
235
301
|
try {
|
|
236
302
|
return context.json(
|
|
237
|
-
|
|
303
|
+
await mutationResponse(
|
|
304
|
+
deps,
|
|
305
|
+
grant,
|
|
238
306
|
await publishWorkspaceArtifactVersion(deps.db, {
|
|
239
307
|
accountId: grant.accountId,
|
|
240
308
|
workspaceId,
|
|
@@ -248,21 +316,30 @@ export function registerWorkspaceArtifactRoutes(app: Hono, deps: ApiRouteDeps):
|
|
|
248
316
|
),
|
|
249
317
|
);
|
|
250
318
|
} catch (error) {
|
|
251
|
-
return
|
|
319
|
+
return workspaceArtifactErrorResponse(context, error);
|
|
252
320
|
}
|
|
253
321
|
});
|
|
254
322
|
|
|
255
323
|
app.post(`${base}/:artifactId/rollback`, async (context) => {
|
|
256
324
|
const workspaceId = context.req.param("workspaceId");
|
|
257
|
-
const
|
|
325
|
+
const authorization = await requireAccessGrantAuthorization(
|
|
326
|
+
context,
|
|
327
|
+
deps,
|
|
328
|
+
workspaceId,
|
|
329
|
+
"artifacts:publish",
|
|
330
|
+
);
|
|
331
|
+
const grant = authorization.grant;
|
|
258
332
|
const request = await body(context, RollbackWorkspaceArtifactRequest);
|
|
333
|
+
const id = artifactId(context);
|
|
259
334
|
try {
|
|
260
335
|
return context.json(
|
|
261
|
-
|
|
336
|
+
await mutationResponse(
|
|
337
|
+
deps,
|
|
338
|
+
grant,
|
|
262
339
|
await rollbackWorkspaceArtifact(deps.db, {
|
|
263
340
|
accountId: grant.accountId,
|
|
264
341
|
workspaceId,
|
|
265
|
-
artifactId:
|
|
342
|
+
artifactId: id,
|
|
266
343
|
versionId: request.versionId,
|
|
267
344
|
expectedCurrentVersionId: request.expectedCurrentVersionId,
|
|
268
345
|
reason: request.reason,
|
|
@@ -271,7 +348,39 @@ export function registerWorkspaceArtifactRoutes(app: Hono, deps: ApiRouteDeps):
|
|
|
271
348
|
),
|
|
272
349
|
);
|
|
273
350
|
} catch (error) {
|
|
274
|
-
return
|
|
351
|
+
return workspaceArtifactErrorResponse(context, error);
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
app.patch(`${base}/:artifactId/status`, async (context) => {
|
|
356
|
+
const workspaceId = context.req.param("workspaceId");
|
|
357
|
+
const authorization = await requireAccessGrantAuthorization(
|
|
358
|
+
context,
|
|
359
|
+
deps,
|
|
360
|
+
workspaceId,
|
|
361
|
+
"artifacts:publish",
|
|
362
|
+
);
|
|
363
|
+
const grant = authorization.grant;
|
|
364
|
+
const request = await body(context, SetWorkspaceArtifactStatusRequest);
|
|
365
|
+
const id = artifactId(context);
|
|
366
|
+
try {
|
|
367
|
+
return context.json(
|
|
368
|
+
await mutationResponse(
|
|
369
|
+
deps,
|
|
370
|
+
grant,
|
|
371
|
+
await setWorkspaceArtifactStatus(deps.db, {
|
|
372
|
+
accountId: grant.accountId,
|
|
373
|
+
workspaceId,
|
|
374
|
+
artifactId: id,
|
|
375
|
+
status: request.status,
|
|
376
|
+
expectedCurrentVersionId: request.expectedCurrentVersionId,
|
|
377
|
+
reason: request.reason,
|
|
378
|
+
...provenance(grant.subjectId, request.idempotencyKey),
|
|
379
|
+
}),
|
|
380
|
+
),
|
|
381
|
+
);
|
|
382
|
+
} catch (error) {
|
|
383
|
+
return workspaceArtifactErrorResponse(context, error);
|
|
275
384
|
}
|
|
276
385
|
});
|
|
277
386
|
}
|