@noodleseed/one 0.174.0 → 0.175.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/commands/solutions-activity.js +1 -1
- package/dist/commands/solutions-activity.js.map +1 -1
- package/dist/commands/solutions-drafts.d.ts.map +1 -1
- package/dist/commands/solutions-drafts.js +36 -9
- package/dist/commands/solutions-drafts.js.map +1 -1
- package/dist/commands/solutions-native-lifecycle.d.ts +5 -0
- package/dist/commands/solutions-native-lifecycle.d.ts.map +1 -0
- package/dist/commands/solutions-native-lifecycle.js +77 -0
- package/dist/commands/solutions-native-lifecycle.js.map +1 -0
- package/dist/commands/solutions-ops.d.ts.map +1 -1
- package/dist/commands/solutions-ops.js +3 -0
- package/dist/commands/solutions-ops.js.map +1 -1
- package/dist/commands/solutions-workspace.d.ts.map +1 -1
- package/dist/commands/solutions-workspace.js +36 -11
- package/dist/commands/solutions-workspace.js.map +1 -1
- package/dist/commands/whatsapp-output.d.ts.map +1 -1
- package/dist/commands/whatsapp-output.js +20 -4
- package/dist/commands/whatsapp-output.js.map +1 -1
- package/node_modules/@noodle-borg/admission-limits/dist/public-record-admission.d.ts +16 -9
- package/node_modules/@noodle-borg/admission-limits/dist/public-record-admission.js +5 -2
- package/node_modules/@noodle-borg/agent-kit/dist/curated/error-fixes.js +9 -0
- package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +5 -5
- package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.js +9 -0
- package/node_modules/@noodle-borg/agent-kit/dist/skill-authoring-refs.js +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-ref.js +13 -1
- package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-coordinator.d.ts +5 -3
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-coordinator.js +5 -4
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-inbox.js +6 -2
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-store.d.ts +1 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-types.d.ts +12 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-work.d.ts +7 -2
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-work.js +14 -4
- package/node_modules/@noodle-borg/assistant-gateway/dist/collection-capture.js +104 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/collection-judgment.js +93 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/collection-ledger.d.ts +105 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/collection-ledger.js +183 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/collection-model-call.js +38 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/collection-parsers.d.ts +22 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/collection-parsers.js +109 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/collection-review.d.ts +21 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/collection-review.js +44 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/collection-spec.d.ts +18 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/collection-spec.js +86 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/collection-store.d.ts +11 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/collection-store.js +18 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/messaging-tool-dispatch.d.ts +20 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/messaging-tool-dispatch.js +38 -2
- package/node_modules/@noodle-borg/assistant-gateway/dist/model-runtime.js +3 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/portable.d.ts +5 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/portable.js +5 -0
- package/node_modules/@noodle-borg/authoring/dist/index.d.ts +1 -0
- package/node_modules/@noodle-borg/authoring/dist/interaction.d.ts +49 -0
- package/node_modules/@noodle-borg/authoring/dist/interaction.js +25 -0
- package/node_modules/@noodle-borg/authoring/dist/server.d.ts +3 -0
- package/node_modules/@noodle-borg/authoring/dist/server.js +4 -0
- package/node_modules/@noodle-borg/authoring/package.json +6 -1
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-channels.js +1 -1
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solution-drafts.js +7 -0
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solution-native-lifecycle.js +43 -0
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solution-records.js +154 -0
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solution-workspace.js +8 -1
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solutions.js +2 -149
- package/node_modules/@noodle-borg/compiler/dist/artifact/assemble.js +2 -1
- package/node_modules/@noodle-borg/compiler/dist/artifact/types.d.ts +33 -0
- package/node_modules/@noodle-borg/compiler/dist/artifact/version.d.ts +2 -1
- package/node_modules/@noodle-borg/compiler/dist/artifact/version.js +2 -1
- package/node_modules/@noodle-borg/compiler/dist/compile.js +5 -0
- package/node_modules/@noodle-borg/compiler/dist/errors.d.ts +1 -1
- package/node_modules/@noodle-borg/compiler/dist/index.d.ts +2 -2
- package/node_modules/@noodle-borg/compiler/dist/index.js +1 -1
- package/node_modules/@noodle-borg/compiler/dist/manifest/interaction-schema.d.ts +41 -0
- package/node_modules/@noodle-borg/compiler/dist/manifest/interaction-schema.js +62 -0
- package/node_modules/@noodle-borg/compiler/dist/manifest/interaction-validation.js +220 -0
- package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +60 -4
- package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +7 -1
- package/node_modules/@noodle-borg/compiler/dist/manifest/website-projection.d.ts +9 -1
- package/node_modules/@noodle-borg/compiler/dist/manifest/website-projection.js +58 -10
- package/node_modules/@noodle-borg/control-plane/dist/in-memory-atomic-state.d.ts +8 -0
- package/node_modules/@noodle-borg/control-plane/dist/in-memory-atomic-state.js +136 -0
- package/node_modules/@noodle-borg/control-plane/dist/in-memory-control-plane-store.d.ts +7 -0
- package/node_modules/@noodle-borg/control-plane/dist/in-memory-control-plane-store.js +16 -24
- package/node_modules/@noodle-borg/control-plane/dist/in-memory-organization-store.d.ts +4 -2
- package/node_modules/@noodle-borg/control-plane/dist/in-memory-organization-store.js +8 -6
- package/node_modules/@noodle-borg/control-plane/dist/mcp-subdomain-claims.d.ts +2 -1
- package/node_modules/@noodle-borg/control-plane/dist/mcp-subdomain-claims.js +13 -8
- package/node_modules/@noodle-borg/control-plane/dist/organization-agreements.d.ts +13 -4
- package/node_modules/@noodle-borg/control-plane/dist/organization-agreements.js +23 -7
- package/node_modules/@noodle-borg/control-plane/dist/portable.d.ts +2 -1
- package/node_modules/@noodle-borg/control-plane/dist/portable.js +1 -0
- package/node_modules/@noodle-borg/module/dist/platform-human-identity.d.ts +5 -2
- package/node_modules/@noodle-borg/runtime/dist/connector/types.d.ts +16 -4
- package/node_modules/@noodle-borg/runtime/dist/execute.d.ts +3 -6
- package/node_modules/@noodle-borg/runtime/dist/index.d.ts +1 -1
- package/node_modules/@noodle-borg/service/dist/application-activity.js +85 -63
- package/node_modules/@noodle-borg/service/dist/application-drafts/contracts.js +9 -0
- package/node_modules/@noodle-borg/service/dist/application-drafts/paths.js +2 -2
- package/node_modules/@noodle-borg/service/dist/application-runtime-target.js +1 -1
- package/node_modules/@noodle-borg/service/dist/business-information/contracts.d.ts +18 -4
- package/node_modules/@noodle-borg/service/dist/business-information/in-memory-business-pages.js +3 -5
- package/node_modules/@noodle-borg/service/dist/business-information/in-memory-installation-lifecycle.js +8 -6
- package/node_modules/@noodle-borg/service/dist/business-information/in-memory-native-lifecycle.js +80 -0
- package/node_modules/@noodle-borg/service/dist/business-information/in-memory-store.js +27 -31
- package/node_modules/@noodle-borg/service/dist/business-information/memory-native-retention.js +29 -0
- package/node_modules/@noodle-borg/service/dist/business-information/model.js +9 -2
- package/node_modules/@noodle-borg/service/dist/business-information/native-query.js +3 -1
- package/node_modules/@noodle-borg/service/dist/business-information/native-record-lifecycle.d.ts +27 -0
- package/node_modules/@noodle-borg/service/dist/business-information/native-record-lifecycle.js +63 -0
- package/node_modules/@noodle-borg/service/dist/business-information/principal-authority.d.ts +3 -1
- package/node_modules/@noodle-borg/service/dist/business-information/principal-authority.js +9 -4
- package/node_modules/@noodle-borg/service/dist/business-information/staff-authority.d.ts +16 -0
- package/node_modules/@noodle-borg/service/dist/business-information/staff-authority.js +60 -0
- package/node_modules/@noodle-borg/service/dist/business-information/workspace-permissions.js +18 -0
- package/node_modules/@noodle-borg/service/dist/business-information-runtime.js +1 -0
- package/node_modules/@noodle-borg/service/dist/business-onboarding.js +16 -3
- package/node_modules/@noodle-borg/service/dist/business-workspaces/contracts.d.ts +110 -0
- package/node_modules/@noodle-borg/service/dist/business-workspaces/permissions.d.ts +7 -0
- package/node_modules/@noodle-borg/service/dist/business-workspaces/store.d.ts +102 -0
- package/node_modules/@noodle-borg/service/dist/channels/360dialog.js +70 -8
- package/node_modules/@noodle-borg/service/dist/channels/capability-report.js +73 -0
- package/node_modules/@noodle-borg/service/dist/channels/collection-turn.js +232 -0
- package/node_modules/@noodle-borg/service/dist/channels/composition.js +1 -0
- package/node_modules/@noodle-borg/service/dist/channels/messaging-eligibility.js +45 -0
- package/node_modules/@noodle-borg/service/dist/channels/reply-buttons.js +23 -0
- package/node_modules/@noodle-borg/service/dist/channels/runtime.js +127 -33
- package/node_modules/@noodle-borg/service/dist/native-record-connector.js +19 -4
- package/node_modules/@noodle-borg/service/dist/routes/application-drafts.js +44 -2
- package/node_modules/@noodle-borg/service/dist/routes/assistant-agent.js +4 -0
- package/node_modules/@noodle-borg/service/dist/routes/business-authoring-dispatch.js +1 -1
- package/node_modules/@noodle-borg/service/dist/routes/business-information-access.js +39 -0
- package/node_modules/@noodle-borg/service/dist/routes/business-information-activity.js +1 -1
- package/node_modules/@noodle-borg/service/dist/routes/business-information-channels.js +2 -2
- package/node_modules/@noodle-borg/service/dist/routes/business-information-connections.js +10 -4
- package/node_modules/@noodle-borg/service/dist/routes/business-information-dispatch.js +11 -0
- package/node_modules/@noodle-borg/service/dist/routes/business-information-installation.js +3 -1
- package/node_modules/@noodle-borg/service/dist/routes/business-information-notice.js +6 -2
- package/node_modules/@noodle-borg/service/dist/routes/business-information-pages.js +1 -1
- package/node_modules/@noodle-borg/service/dist/routes/business-information-paths.js +1 -1
- package/node_modules/@noodle-borg/service/dist/routes/business-information-record-create.js +7 -2
- package/node_modules/@noodle-borg/service/dist/routes/business-information-record-reads.js +2 -2
- package/node_modules/@noodle-borg/service/dist/routes/business-information-records.js +332 -0
- package/node_modules/@noodle-borg/service/dist/routes/business-information-settings.js +6 -3
- package/node_modules/@noodle-borg/service/dist/routes/business-information-staff.js +9 -5
- package/node_modules/@noodle-borg/service/dist/routes/business-information.js +12 -356
- package/node_modules/@noodle-borg/service/dist/routes/business-native-lifecycle.js +46 -0
- package/node_modules/@noodle-borg/service/dist/routes/business-workspaces.js +14 -1
- package/node_modules/@noodle-borg/service/dist/service.js +1 -0
- package/node_modules/@noodle-borg/service/dist/web-capabilities.js +3 -1
- package/node_modules/@noodle-borg/service/package.json +2 -1
- package/node_modules/@noodle-borg/wire-contracts/dist/application-draft-validation.d.ts +59 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/application-draft-validation.js +38 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/business-information.d.ts +144 -50
- package/node_modules/@noodle-borg/wire-contracts/dist/business-information.js +44 -4
- package/node_modules/@noodle-borg/wire-contracts/dist/business-workspaces.d.ts +70 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/business-workspaces.js +31 -1
- package/node_modules/@noodle-borg/wire-contracts/dist/index.d.ts +2 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/index.js +2 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/native-record-lifecycle.d.ts +81 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/native-record-lifecycle.js +39 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/whatsapp.d.ts +26 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/whatsapp.js +24 -1
- package/package.json +2 -2
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { readJsonBody, sendJson } from '@noodle-borg/transport-http';
|
|
2
|
+
import { ManagedRecordCreateRequestSchema, ManagedRecordMutationRequestSchema, ManagedRecordStatusSchema, } from '@noodle-borg/wire-contracts';
|
|
3
|
+
import { admitBusinessMutation } from '../business-api-admission.js';
|
|
4
|
+
import { businessGrantAllows, PayloadValidationError, } from '../business-information/portable.js';
|
|
5
|
+
import { enabledCollection, idempotencyConflict, invalid, methodNotAllowed, mutationFailure, now, } from './business-information.js';
|
|
6
|
+
import { requireIdentity, requireInstallationPermission, resolveBusinessStaffGrant, runBusinessStaffOperation, } from './business-information-access.js';
|
|
7
|
+
import { loadExternalRecord, sendExternalRecordPage, sourceBindingKey, unsupportedExternalOperation, } from './business-information-external.js';
|
|
8
|
+
import { createNativeRecord } from './business-information-record-create.js';
|
|
9
|
+
import { cursorRequest, sendNativeRecordActivity } from './business-information-record-reads.js';
|
|
10
|
+
import { expectedRevision, mutationPermission, parseNativeRecordQuery, parseBusinessPaging as parsePaging, requestIdempotencyKey, } from './business-information-request.js';
|
|
11
|
+
import { collectionToWire, externalRecordToWire, recordDetailToWire, recordToWire, } from './business-information-wire.js';
|
|
12
|
+
export async function handleManagedRecords(req, res, url, ref, deps) {
|
|
13
|
+
const identity = await requireIdentity(req, res, deps);
|
|
14
|
+
if (identity === false)
|
|
15
|
+
return;
|
|
16
|
+
if (ref.collection === undefined)
|
|
17
|
+
return sendJson(res, 404, { error: 'not found' });
|
|
18
|
+
const collection = ref.collection;
|
|
19
|
+
if (ref.action === 'activity' && req.method === 'GET' && ref.recordId !== undefined) {
|
|
20
|
+
const authorized = await requireInstallationPermission(res, ref, identity, 'records:read', deps);
|
|
21
|
+
if (authorized === undefined)
|
|
22
|
+
return;
|
|
23
|
+
const definition = enabledCollection(res, authorized.installation, collection);
|
|
24
|
+
if (definition === undefined)
|
|
25
|
+
return;
|
|
26
|
+
if (definition.authority.authority === 'external') {
|
|
27
|
+
return sendJson(res, 200, { ok: true, data: { activities: [] } });
|
|
28
|
+
}
|
|
29
|
+
return sendNativeRecordActivity(res, url, authorized.installation.scope, collection, ref.recordId, deps.store, identity.subject);
|
|
30
|
+
}
|
|
31
|
+
if (ref.action === 'export' && req.method === 'GET') {
|
|
32
|
+
const authorized = await requireInstallationPermission(res, ref, identity, 'records:export', deps);
|
|
33
|
+
if (authorized === undefined)
|
|
34
|
+
return;
|
|
35
|
+
const definition = enabledCollection(res, authorized.installation, collection);
|
|
36
|
+
if (definition === undefined)
|
|
37
|
+
return;
|
|
38
|
+
const paging = parsePaging(url, 500);
|
|
39
|
+
if (!paging.ok)
|
|
40
|
+
return sendJson(res, 400, { error: paging.error });
|
|
41
|
+
if (definition.authority.authority === 'external') {
|
|
42
|
+
return sendExternalRecordPage(res, authorized.installation, definition, { ...paging.value, limit: paging.value.limit ?? 500 }, deps);
|
|
43
|
+
}
|
|
44
|
+
const page = await cursorRequest(res, () => runBusinessStaffOperation(deps, authorized.scope, identity.subject, 'records:export', () => deps.store.exportRequests({
|
|
45
|
+
scope: authorized.installation.scope,
|
|
46
|
+
collectionKey: collection,
|
|
47
|
+
...paging.value,
|
|
48
|
+
includeDeleted: url.searchParams.get('includeDeleted') === 'true',
|
|
49
|
+
})));
|
|
50
|
+
if (page === undefined)
|
|
51
|
+
return;
|
|
52
|
+
return sendJson(res, 200, {
|
|
53
|
+
ok: true,
|
|
54
|
+
data: {
|
|
55
|
+
records: page.records.map((record) => ({
|
|
56
|
+
...recordToWire(record),
|
|
57
|
+
...(record.content ? { notes: record.content.notes } : {}),
|
|
58
|
+
})),
|
|
59
|
+
...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }),
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
if (ref.recordId === undefined && req.method === 'GET') {
|
|
64
|
+
const authorized = await requireInstallationPermission(res, ref, identity, 'records:read', deps);
|
|
65
|
+
if (authorized === undefined)
|
|
66
|
+
return;
|
|
67
|
+
const definition = enabledCollection(res, authorized.installation, collection);
|
|
68
|
+
if (definition === undefined)
|
|
69
|
+
return;
|
|
70
|
+
const paging = parsePaging(url, 100);
|
|
71
|
+
if (!paging.ok)
|
|
72
|
+
return sendJson(res, 400, { error: paging.error });
|
|
73
|
+
const query = parseNativeRecordQuery(url);
|
|
74
|
+
if (!query.ok)
|
|
75
|
+
return sendJson(res, 400, { error: query.error, code: 'invalid_query' });
|
|
76
|
+
if (definition.authority.authority === 'external') {
|
|
77
|
+
if (Object.keys(query.value).length > 0)
|
|
78
|
+
return sendJson(res, 400, {
|
|
79
|
+
error: 'Field and date queries currently require a native collection.',
|
|
80
|
+
code: 'invalid_query',
|
|
81
|
+
});
|
|
82
|
+
return sendExternalRecordPage(res, authorized.installation, definition, paging.value, deps);
|
|
83
|
+
}
|
|
84
|
+
const statusValue = url.searchParams.get('status');
|
|
85
|
+
const status = statusValue === null ? undefined : ManagedRecordStatusSchema.safeParse(statusValue);
|
|
86
|
+
if (status !== undefined && !status.success)
|
|
87
|
+
return sendJson(res, 400, { error: 'invalid status' });
|
|
88
|
+
const assigneeSubject = url.searchParams.get('assigneeSubject') ?? undefined;
|
|
89
|
+
const page = await cursorRequest(res, () => runBusinessStaffOperation(deps, authorized.scope, identity.subject, 'records:read', () => deps.store.listRequests({
|
|
90
|
+
scope: authorized.installation.scope,
|
|
91
|
+
collectionKey: collection,
|
|
92
|
+
...paging.value,
|
|
93
|
+
...query.value,
|
|
94
|
+
...(status === undefined ? {} : { status: status.data }),
|
|
95
|
+
...(assigneeSubject === undefined ? {} : { assigneeSubject }),
|
|
96
|
+
includeDeleted: url.searchParams.get('includeDeleted') === 'true',
|
|
97
|
+
})));
|
|
98
|
+
if (page === undefined)
|
|
99
|
+
return;
|
|
100
|
+
return sendJson(res, 200, {
|
|
101
|
+
ok: true,
|
|
102
|
+
data: {
|
|
103
|
+
records: page.records.map(recordToWire),
|
|
104
|
+
...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }),
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
if (ref.recordId === undefined && req.method === 'POST') {
|
|
109
|
+
const authorized = await requireInstallationPermission(res, ref, identity, 'records:create', deps);
|
|
110
|
+
if (authorized === undefined)
|
|
111
|
+
return;
|
|
112
|
+
const definition = enabledCollection(res, authorized.installation, collection);
|
|
113
|
+
if (definition === undefined)
|
|
114
|
+
return;
|
|
115
|
+
if (definition.authority.authority === 'external') {
|
|
116
|
+
return unsupportedExternalOperation(res, 'create');
|
|
117
|
+
}
|
|
118
|
+
const idempotencyKey = requestIdempotencyKey(req);
|
|
119
|
+
if (idempotencyKey === undefined) {
|
|
120
|
+
return sendJson(res, 400, { error: 'Idempotency-Key header is required (1-128 characters)' });
|
|
121
|
+
}
|
|
122
|
+
const body = await readJsonBody(req, deps.maxBody);
|
|
123
|
+
if (!body.ok)
|
|
124
|
+
return sendJson(res, body.status, { error: body.error });
|
|
125
|
+
const parsed = ManagedRecordCreateRequestSchema.safeParse(body.value);
|
|
126
|
+
if (!parsed.success)
|
|
127
|
+
return invalid(res, parsed.error);
|
|
128
|
+
const result = await createNativeRecord(res, deps, {
|
|
129
|
+
installation: authorized.installation,
|
|
130
|
+
collection,
|
|
131
|
+
idempotencyKey,
|
|
132
|
+
payload: parsed.data.payload,
|
|
133
|
+
origin: { kind: 'portal' },
|
|
134
|
+
actorSubject: identity.subject,
|
|
135
|
+
admit: () => admitBusinessMutation(res, authorized.scope),
|
|
136
|
+
});
|
|
137
|
+
if (result === undefined)
|
|
138
|
+
return;
|
|
139
|
+
if (result.disposition === 'conflict')
|
|
140
|
+
return idempotencyConflict(res);
|
|
141
|
+
if (result.disposition === 'paused') {
|
|
142
|
+
return sendJson(res, 503, {
|
|
143
|
+
error: 'Record creation requires an active application.',
|
|
144
|
+
code: 'application_unavailable',
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
return sendJson(res, result.disposition === 'created' ? 201 : 200, {
|
|
148
|
+
ok: true,
|
|
149
|
+
data: { record: recordToWire(result.record) },
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
if (ref.recordId !== undefined && req.method === 'GET') {
|
|
153
|
+
const recordId = ref.recordId;
|
|
154
|
+
const authorized = await requireInstallationPermission(res, ref, identity, 'records:read', deps);
|
|
155
|
+
if (authorized === undefined)
|
|
156
|
+
return;
|
|
157
|
+
const definition = enabledCollection(res, authorized.installation, collection);
|
|
158
|
+
if (definition === undefined)
|
|
159
|
+
return;
|
|
160
|
+
if (definition.authority.authority === 'external') {
|
|
161
|
+
const loaded = await loadExternalRecord(authorized.installation, definition, ref.recordId, deps);
|
|
162
|
+
if (loaded === undefined)
|
|
163
|
+
return sendJson(res, 503, { error: 'collection source unavailable' });
|
|
164
|
+
if (loaded.record === undefined)
|
|
165
|
+
return sendJson(res, 404, { error: 'record not found' });
|
|
166
|
+
return sendJson(res, 200, {
|
|
167
|
+
ok: true,
|
|
168
|
+
data: {
|
|
169
|
+
record: externalRecordToWire(loaded.record, loaded.binding),
|
|
170
|
+
collection: collectionToWire(definition),
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
const record = await runBusinessStaffOperation(deps, authorized.scope, identity.subject, 'records:read', () => deps.store.getRequest(authorized.installation.scope, collection, recordId));
|
|
175
|
+
if (record === undefined)
|
|
176
|
+
return sendJson(res, 404, { error: 'record not found' });
|
|
177
|
+
return sendJson(res, 200, {
|
|
178
|
+
ok: true,
|
|
179
|
+
data: recordDetailToWire(authorized.installation, record),
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
if (ref.recordId !== undefined && req.method === 'PATCH') {
|
|
183
|
+
const recordId = ref.recordId;
|
|
184
|
+
const body = await readJsonBody(req, deps.maxBody);
|
|
185
|
+
if (!body.ok)
|
|
186
|
+
return sendJson(res, body.status, { error: body.error });
|
|
187
|
+
const parsed = ManagedRecordMutationRequestSchema.safeParse(body.value);
|
|
188
|
+
if (!parsed.success)
|
|
189
|
+
return invalid(res, parsed.error);
|
|
190
|
+
const permission = mutationPermission(parsed.data.operation);
|
|
191
|
+
const authorized = await requireInstallationPermission(res, ref, identity, permission, deps);
|
|
192
|
+
if (authorized === undefined)
|
|
193
|
+
return;
|
|
194
|
+
const definition = enabledCollection(res, authorized.installation, collection);
|
|
195
|
+
if (definition === undefined)
|
|
196
|
+
return;
|
|
197
|
+
if (definition.authority.authority === 'external') {
|
|
198
|
+
return unsupportedExternalOperation(res, parsed.data.operation);
|
|
199
|
+
}
|
|
200
|
+
if (parsed.data.operation === 'migrate-schema') {
|
|
201
|
+
const result = await runBusinessStaffOperation(deps, authorized.scope, identity.subject, permission, () => deps.store.migrateLegacyRequest({
|
|
202
|
+
scope: authorized.installation.scope,
|
|
203
|
+
collectionKey: collection,
|
|
204
|
+
id: recordId,
|
|
205
|
+
expectedRevision: parsed.data.expectedRevision,
|
|
206
|
+
actorSubject: identity.subject,
|
|
207
|
+
}));
|
|
208
|
+
if (!result.ok)
|
|
209
|
+
return mutationFailure(res, result);
|
|
210
|
+
return sendJson(res, 200, { ok: true, data: { record: recordToWire(result.record) } });
|
|
211
|
+
}
|
|
212
|
+
let operation;
|
|
213
|
+
if (parsed.data.operation === 'update') {
|
|
214
|
+
operation = {
|
|
215
|
+
kind: 'update',
|
|
216
|
+
payload: parsed.data.patch,
|
|
217
|
+
...(parsed.data.unset ? { unset: parsed.data.unset } : {}),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
else if (parsed.data.operation === 'assign') {
|
|
221
|
+
if (parsed.data.assigneeSubject !== null) {
|
|
222
|
+
const assigneeGrant = await resolveBusinessStaffGrant(deps, authorized.installation.scope, parsed.data.assigneeSubject);
|
|
223
|
+
if (assigneeGrant === undefined ||
|
|
224
|
+
assigneeGrant.revokedAt !== undefined ||
|
|
225
|
+
!businessGrantAllows(assigneeGrant, 'records:assign')) {
|
|
226
|
+
return sendJson(res, 422, {
|
|
227
|
+
error: 'assignee must have an active operator grant',
|
|
228
|
+
code: 'invalid_assignee',
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
operation = {
|
|
233
|
+
kind: 'assign',
|
|
234
|
+
assigneeSubject: parsed.data.assigneeSubject ?? undefined,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
else if (parsed.data.operation === 'set-status') {
|
|
238
|
+
operation = { kind: 'set_status', status: parsed.data.status };
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
operation = { kind: 'add_note', note: parsed.data.note };
|
|
242
|
+
}
|
|
243
|
+
let result;
|
|
244
|
+
try {
|
|
245
|
+
result = await runBusinessStaffOperation(deps, authorized.scope, identity.subject, permission, () => deps.store.mutateRequest({
|
|
246
|
+
scope: authorized.installation.scope,
|
|
247
|
+
collectionKey: collection,
|
|
248
|
+
id: recordId,
|
|
249
|
+
expectedRevision: parsed.data.expectedRevision,
|
|
250
|
+
actorSubject: identity.subject,
|
|
251
|
+
operation,
|
|
252
|
+
}));
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
if (error instanceof PayloadValidationError) {
|
|
256
|
+
return sendJson(res, 400, { error: error.message, code: error.code });
|
|
257
|
+
}
|
|
258
|
+
throw error;
|
|
259
|
+
}
|
|
260
|
+
if (!result.ok)
|
|
261
|
+
return mutationFailure(res, result);
|
|
262
|
+
return sendJson(res, 200, { ok: true, data: { record: recordToWire(result.record) } });
|
|
263
|
+
}
|
|
264
|
+
if (ref.recordId !== undefined && req.method === 'DELETE') {
|
|
265
|
+
const recordId = ref.recordId;
|
|
266
|
+
const authorized = await requireInstallationPermission(res, ref, identity, 'records:delete', deps);
|
|
267
|
+
if (authorized === undefined)
|
|
268
|
+
return;
|
|
269
|
+
const definition = enabledCollection(res, authorized.installation, collection);
|
|
270
|
+
if (definition === undefined)
|
|
271
|
+
return;
|
|
272
|
+
const body = await readJsonBody(req, deps.maxBody);
|
|
273
|
+
if (!body.ok)
|
|
274
|
+
return sendJson(res, body.status, { error: body.error });
|
|
275
|
+
const revision = expectedRevision(body.value);
|
|
276
|
+
if (revision === undefined) {
|
|
277
|
+
return sendJson(res, 400, { error: 'expectedRevision must be a positive integer' });
|
|
278
|
+
}
|
|
279
|
+
if (definition.authority.authority === 'external') {
|
|
280
|
+
const loaded = await loadExternalRecord(authorized.installation, definition, ref.recordId, deps);
|
|
281
|
+
if (loaded === undefined)
|
|
282
|
+
return sendJson(res, 503, { error: 'collection source unavailable' });
|
|
283
|
+
if (loaded.record === undefined)
|
|
284
|
+
return sendJson(res, 404, { error: 'record not found' });
|
|
285
|
+
const { record } = loaded;
|
|
286
|
+
if (record.revision !== revision) {
|
|
287
|
+
return sendJson(res, 409, {
|
|
288
|
+
error: 'record revision conflict',
|
|
289
|
+
code: 'revision_conflict',
|
|
290
|
+
currentRevision: record.revision,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
await deps.sourceStore?.suppressExternalRecord({
|
|
294
|
+
...sourceBindingKey(authorized.installation, definition),
|
|
295
|
+
sourceId: record.source.id,
|
|
296
|
+
reason: 'customer_request',
|
|
297
|
+
now: now(deps),
|
|
298
|
+
});
|
|
299
|
+
const deletedAt = now(deps).toISOString();
|
|
300
|
+
return sendJson(res, 200, {
|
|
301
|
+
ok: true,
|
|
302
|
+
data: {
|
|
303
|
+
recordId: record.id,
|
|
304
|
+
authority: 'external',
|
|
305
|
+
disposition: 'suppressed',
|
|
306
|
+
deletedAt,
|
|
307
|
+
},
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
const result = await runBusinessStaffOperation(deps, authorized.scope, identity.subject, 'records:delete', () => deps.store.deleteRequest({
|
|
311
|
+
scope: authorized.installation.scope,
|
|
312
|
+
collectionKey: collection,
|
|
313
|
+
id: recordId,
|
|
314
|
+
expectedRevision: revision,
|
|
315
|
+
actorSubject: identity.subject,
|
|
316
|
+
reason: 'customer_request',
|
|
317
|
+
}));
|
|
318
|
+
if (!result.ok)
|
|
319
|
+
return mutationFailure(res, result);
|
|
320
|
+
return sendJson(res, 200, {
|
|
321
|
+
ok: true,
|
|
322
|
+
data: {
|
|
323
|
+
recordId: result.record.id,
|
|
324
|
+
authority: 'native',
|
|
325
|
+
disposition: 'deleted',
|
|
326
|
+
deletedAt: result.record.deletedAt,
|
|
327
|
+
},
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
return methodNotAllowed(res);
|
|
331
|
+
}
|
|
332
|
+
//# sourceMappingURL=business-information-records.js.map
|
|
@@ -26,10 +26,13 @@ export async function handleBusinessSettings(req, res, ref, deps) {
|
|
|
26
26
|
res.setHeader('cache-control', 'private, no-store');
|
|
27
27
|
try {
|
|
28
28
|
if (req.method === 'GET') {
|
|
29
|
-
await
|
|
29
|
+
const data = await deps.store.staff.run(authorized.scope, identity.subject, 'records:read', async () => {
|
|
30
|
+
await settings.initialize(target);
|
|
31
|
+
return settings.read(target, canEdit);
|
|
32
|
+
});
|
|
30
33
|
return sendJson(res, 200, ApplicationSettingsResponseSchema.parse({
|
|
31
34
|
ok: true,
|
|
32
|
-
data
|
|
35
|
+
data,
|
|
33
36
|
}));
|
|
34
37
|
}
|
|
35
38
|
const body = await readJsonBody(req, deps.maxBody);
|
|
@@ -39,7 +42,7 @@ export async function handleBusinessSettings(req, res, ref, deps) {
|
|
|
39
42
|
if (!parsed.success)
|
|
40
43
|
return sendJson(res, 400, { code: 'settings_invalid', error: 'Invalid settings update.' });
|
|
41
44
|
const registry = deps.registry;
|
|
42
|
-
const data = await settings.save(target, parsed.data, identity.subject, registry ? () => resolveSettingsTargets(registry, target.scope, deps.store) : undefined);
|
|
45
|
+
const data = await deps.store.staff.run(authorized.scope, identity.subject, 'installation:administer', () => settings.save(target, parsed.data, identity.subject, registry ? () => resolveSettingsTargets(registry, target.scope, deps.store) : undefined));
|
|
43
46
|
await deps.audit?.emit({
|
|
44
47
|
eventType: 'config.variable.business_settings_changed',
|
|
45
48
|
org: authorized.scope.org,
|
|
@@ -21,7 +21,13 @@ export async function handleMySolutionInstallations(req, res, deps) {
|
|
|
21
21
|
return sendJson(res, 400, { error: 'cursor is invalid' });
|
|
22
22
|
}
|
|
23
23
|
const limit = paging.value.limit ?? 100;
|
|
24
|
-
const
|
|
24
|
+
const current = [];
|
|
25
|
+
for (const { installation } of await deps.store.listInstallationsForSubject(identity.subject)) {
|
|
26
|
+
const grant = await deps.store.staff.resolve(installation.scope, identity.subject);
|
|
27
|
+
if (grant && !grant.revokedAt)
|
|
28
|
+
current.push({ installation, grant });
|
|
29
|
+
}
|
|
30
|
+
const joined = current
|
|
25
31
|
.sort((left, right) => compareDiscoveryRows(left, right))
|
|
26
32
|
.filter((item) => after === undefined || discoveryOrderKey(item) > after)
|
|
27
33
|
.slice(0, limit + 1);
|
|
@@ -201,13 +207,11 @@ export async function handleEligibleBusinessAssignees(req, res, ref, deps) {
|
|
|
201
207
|
const authorized = await requireInstallationPermission(res, ref, identity, 'records:assign', deps);
|
|
202
208
|
if (authorized === undefined)
|
|
203
209
|
return;
|
|
204
|
-
const
|
|
210
|
+
const assignees = await deps.store.listEligibleAssignees(authorized.scope, identity.subject);
|
|
205
211
|
sendJson(res, 200, {
|
|
206
212
|
ok: true,
|
|
207
213
|
data: {
|
|
208
|
-
assignees:
|
|
209
|
-
.map((grant) => ({ subject: grant.subject, email: grant.email, role: grant.role }))
|
|
210
|
-
.slice(0, 100),
|
|
214
|
+
assignees: assignees.slice(0, 100),
|
|
211
215
|
},
|
|
212
216
|
});
|
|
213
217
|
}
|