@vertesia/common 1.5.0-dev.20260804.124748Z → 1.5.0-dev.20260807.073259Z
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/lib/Progress.d.ts.map +1 -1
- package/lib/Progress.js.map +1 -1
- package/lib/access-control-values.d.ts +2 -2
- package/lib/access-control-values.d.ts.map +1 -1
- package/lib/account-values.d.ts +1 -1
- package/lib/account-values.d.ts.map +1 -1
- package/lib/api-schemas/access-control.d.ts +19 -19
- package/lib/api-schemas/adapter.d.ts +1 -0
- package/lib/api-schemas/adapter.d.ts.map +1 -1
- package/lib/api-schemas/adapter.js +1 -1
- package/lib/api-schemas/adapter.js.map +1 -1
- package/lib/api-schemas/agent-runs.d.ts +660 -515
- package/lib/api-schemas/agent-runs.d.ts.map +1 -1
- package/lib/api-schemas/agent-runs.js +11 -2
- package/lib/api-schemas/agent-runs.js.map +1 -1
- package/lib/api-schemas/analytics.d.ts +12 -12
- package/lib/api-schemas/apikey.d.ts +10 -10
- package/lib/api-schemas/app-lifecycle.d.ts +186 -152
- package/lib/api-schemas/app-lifecycle.d.ts.map +1 -1
- package/lib/api-schemas/app-lifecycle.js +51 -0
- package/lib/api-schemas/app-lifecycle.js.map +1 -1
- package/lib/api-schemas/app-runtime.d.ts +4493 -2122
- package/lib/api-schemas/app-runtime.d.ts.map +1 -1
- package/lib/api-schemas/app-runtime.js +93 -1
- package/lib/api-schemas/app-runtime.js.map +1 -1
- package/lib/api-schemas/apps.d.ts +43 -19
- package/lib/api-schemas/apps.d.ts.map +1 -1
- package/lib/api-schemas/apps.js +63 -3
- package/lib/api-schemas/apps.js.map +1 -1
- package/lib/api-schemas/audit-trail.d.ts +129 -129
- package/lib/api-schemas/bulk-operation.d.ts +10 -10
- package/lib/api-schemas/content.d.ts +542 -398
- package/lib/api-schemas/content.d.ts.map +1 -1
- package/lib/api-schemas/content.js +12 -0
- package/lib/api-schemas/content.js.map +1 -1
- package/lib/api-schemas/cost-analytics.d.ts +12 -12
- package/lib/api-schemas/dashboard.d.ts +10 -10
- package/lib/api-schemas/data-store.d.ts +556 -556
- package/lib/api-schemas/document-processing.d.ts +280 -196
- package/lib/api-schemas/document-processing.d.ts.map +1 -1
- package/lib/api-schemas/embeddings.d.ts +12 -12
- package/lib/api-schemas/environment.d.ts +20 -18
- package/lib/api-schemas/environment.d.ts.map +1 -1
- package/lib/api-schemas/events.d.ts +988 -698
- package/lib/api-schemas/events.d.ts.map +1 -1
- package/lib/api-schemas/events.js +59 -3
- package/lib/api-schemas/events.js.map +1 -1
- package/lib/api-schemas/files.d.ts +1 -1
- package/lib/api-schemas/group.d.ts +3 -3
- package/lib/api-schemas/indexing.d.ts +3 -3
- package/lib/api-schemas/integrations.d.ts +4 -4
- package/lib/api-schemas/interaction.d.ts +3894 -2752
- package/lib/api-schemas/interaction.d.ts.map +1 -1
- package/lib/api-schemas/interaction.js +6 -0
- package/lib/api-schemas/interaction.js.map +1 -1
- package/lib/api-schemas/invites.d.ts +9 -9
- package/lib/api-schemas/oauth-server.d.ts +36 -36
- package/lib/api-schemas/parameters.js.map +1 -1
- package/lib/api-schemas/process.d.ts +47 -47
- package/lib/api-schemas/project-configuration.d.ts +944 -664
- package/lib/api-schemas/project-configuration.d.ts.map +1 -1
- package/lib/api-schemas/project.d.ts +1403 -983
- package/lib/api-schemas/project.d.ts.map +1 -1
- package/lib/api-schemas/registry.d.ts +19119 -13557
- package/lib/api-schemas/registry.d.ts.map +1 -1
- package/lib/api-schemas/registry.js +170 -18
- package/lib/api-schemas/registry.js.map +1 -1
- package/lib/api-schemas/secrets.d.ts +4 -4
- package/lib/api-schemas/store.d.ts +4624 -3224
- package/lib/api-schemas/store.d.ts.map +1 -1
- package/lib/api-schemas/sts.d.ts +46 -36
- package/lib/api-schemas/sts.d.ts.map +1 -1
- package/lib/api-schemas/sts.js +11 -0
- package/lib/api-schemas/sts.js.map +1 -1
- package/lib/api-schemas/task.d.ts +30 -30
- package/lib/api-schemas/tools.d.ts +9 -9
- package/lib/api-schemas/user.d.ts +6 -6
- package/lib/api-schemas/view-execution.d.ts +626 -570
- package/lib/api-schemas/view-execution.d.ts.map +1 -1
- package/lib/api-schemas/views.d.ts +1293 -1125
- package/lib/api-schemas/views.d.ts.map +1 -1
- package/lib/api-schemas/workflow-runs.d.ts +325 -268
- package/lib/api-schemas/workflow-runs.d.ts.map +1 -1
- package/lib/apps.d.ts +38 -24
- package/lib/apps.d.ts.map +1 -1
- package/lib/apps.js +4 -0
- package/lib/apps.js.map +1 -1
- package/lib/audit-trail.d.ts +3 -3
- package/lib/audit-trail.d.ts.map +1 -1
- package/lib/data-platform.d.ts +31 -31
- package/lib/data-platform.d.ts.map +1 -1
- package/lib/environment.d.ts +3 -3
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/platform-event-validation.d.ts.map +1 -1
- package/lib/platform-event-validation.js +15 -2
- package/lib/platform-event-validation.js.map +1 -1
- package/lib/platform-event.d.ts +31 -3
- package/lib/platform-event.d.ts.map +1 -1
- package/lib/platform-event.js.map +1 -1
- package/lib/project.d.ts +1 -1
- package/lib/schema-for-extraction.js.map +1 -1
- package/lib/store/agent-approval.d.ts +1 -1
- package/lib/store/agent-approval.d.ts.map +1 -1
- package/lib/store/intake-policy-schema.generated.d.ts.map +1 -1
- package/lib/store/intake-policy-schema.generated.js +36 -0
- package/lib/store/intake-policy-schema.generated.js.map +1 -1
- package/lib/store/process-schema.d.ts +1 -1
- package/lib/store/process-schema.d.ts.map +1 -1
- package/lib/store/process-validation.js.map +1 -1
- package/lib/store/schedule.d.ts +6 -6
- package/lib/store/schedule.d.ts.map +1 -1
- package/lib/store/store.d.ts +3 -3
- package/lib/store/store.d.ts.map +1 -1
- package/lib/store/workflow.d.ts +6 -0
- package/lib/store/workflow.d.ts.map +1 -1
- package/lib/store/workflow.js +8 -0
- package/lib/store/workflow.js.map +1 -1
- package/lib/sts-errors.d.ts +7 -0
- package/lib/sts-errors.d.ts.map +1 -0
- package/lib/sts-errors.js +11 -0
- package/lib/sts-errors.js.map +1 -0
- package/lib/sts-token-types.d.ts +2 -1
- package/lib/sts-token-types.d.ts.map +1 -1
- package/lib/sts-token-types.js.map +1 -1
- package/lib/user.d.ts +1 -1
- package/lib/user.d.ts.map +1 -1
- package/lib/user.js +1 -1
- package/lib/user.js.map +1 -1
- package/lib/versions.d.ts +10 -0
- package/lib/versions.d.ts.map +1 -1
- package/lib/versions.js +10 -0
- package/lib/versions.js.map +1 -1
- package/lib/vertesia-common.js +3 -3
- package/lib/vertesia-common.js.map +1 -1
- package/lib/view-configuration-validation.js.map +1 -1
- package/lib/view-query-validation.js.map +1 -1
- package/lib/view-validation-helpers.d.ts +1 -1
- package/lib/view-validation-helpers.d.ts.map +1 -1
- package/lib/views-schema.d.ts +51 -51
- package/lib/views-schema.d.ts.map +1 -1
- package/lib/views-validation.js.map +1 -1
- package/lib/views.d.ts +2 -2
- package/lib/views.d.ts.map +1 -1
- package/lib/workflow-analytics.d.ts +10 -3
- package/lib/workflow-analytics.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api-schemas/adapter.ts +1 -1
- package/src/api-schemas/agent-runs.ts +12 -2
- package/src/api-schemas/api-discriminators.test.ts +84 -0
- package/src/api-schemas/app-delete-summary.contract.test.ts +67 -0
- package/src/api-schemas/app-lifecycle.ts +59 -0
- package/src/api-schemas/app-runtime.ts +103 -0
- package/src/api-schemas/apps.ts +72 -3
- package/src/api-schemas/content.ts +13 -0
- package/src/api-schemas/events.ts +63 -3
- package/src/api-schemas/interaction.contract.test.ts +27 -0
- package/src/api-schemas/interaction.ts +7 -0
- package/src/api-schemas/mcp-api-key.contract.test.ts +86 -0
- package/src/api-schemas/registry.ts +200 -18
- package/src/api-schemas/store.contract.test.ts +21 -0
- package/src/api-schemas/sts.contract.test.ts +33 -1
- package/src/api-schemas/sts.ts +13 -0
- package/src/apps.test.ts +82 -0
- package/src/apps.ts +50 -16
- package/src/index.ts +1 -0
- package/src/platform-event-validation.test.ts +26 -0
- package/src/platform-event-validation.ts +15 -2
- package/src/platform-event.ts +43 -0
- package/src/store/intake-policy-schema.generated.ts +36 -0
- package/src/store/workflow.ts +9 -0
- package/src/sts-errors.ts +13 -0
- package/src/sts-token-types.ts +2 -0
- package/src/user.ts +1 -1
- package/src/versions.test.ts +13 -0
- package/src/versions.ts +12 -0
- package/src/workflow-analytics.ts +10 -3
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { McpApiKeyStatus } from '../apps.js';
|
|
3
|
+
import { AppInstallationPayloadSchema } from './app-lifecycle.js';
|
|
4
|
+
import { McpApiKeyStatusSchema, SetMcpApiKeyRequestSchema } from './apps.js';
|
|
5
|
+
import { validateApiRequest } from './registry.js';
|
|
6
|
+
|
|
7
|
+
/** Exact type identity — `extends` in both directions is too weak (any/unknown slip through). */
|
|
8
|
+
type Equals<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? true : false;
|
|
9
|
+
function assertType<T extends true>(_ok: T): void {}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Request bodies are enforced by AJV against the EMITTED JSON Schema, not by parsing with Zod. A
|
|
13
|
+
* Zod-only assertion would pass on transforms (`.trim()`) that leave no trace in the emission and
|
|
14
|
+
* therefore never run at the endpoint — so the blank-key cases below go through `validateApiRequest`,
|
|
15
|
+
* which is the path `validatedBody` actually takes.
|
|
16
|
+
*/
|
|
17
|
+
describe('SetMcpApiKeyRequest — as enforced at the endpoint (AJV)', () => {
|
|
18
|
+
it('accepts a real key', () => {
|
|
19
|
+
expect(validateApiRequest('SetMcpApiKeyRequest', { api_key: 'sk-live-abcdef' }).valid).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('rejects a whitespace-only key', () => {
|
|
23
|
+
expect(validateApiRequest('SetMcpApiKeyRequest', { api_key: ' ' }).valid).toBe(false);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('rejects a tab/newline-only key', () => {
|
|
27
|
+
expect(validateApiRequest('SetMcpApiKeyRequest', { api_key: '\t\n ' }).valid).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('rejects an empty key', () => {
|
|
31
|
+
expect(validateApiRequest('SetMcpApiKeyRequest', { api_key: '' }).valid).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('rejects a missing key', () => {
|
|
35
|
+
expect(validateApiRequest('SetMcpApiKeyRequest', {}).valid).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('rejects unknown properties', () => {
|
|
39
|
+
expect(validateApiRequest('SetMcpApiKeyRequest', { api_key: 'sk-live', collection_id: 'x' }).valid).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('still normalizes surrounding whitespace for Zod consumers', () => {
|
|
43
|
+
expect(SetMcpApiKeyRequestSchema.parse({ api_key: ' sk-live-abcdef ' }).api_key).toBe('sk-live-abcdef');
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('AppInstallationPayload.api_key_params — as enforced at the endpoint (AJV)', () => {
|
|
48
|
+
const APP_ID = '68b1779130afe5403a1589bc';
|
|
49
|
+
|
|
50
|
+
it('accepts a key per collection', () => {
|
|
51
|
+
const result = validateApiRequest('AppInstallationPayload', {
|
|
52
|
+
app_id: APP_ID,
|
|
53
|
+
api_key_params: { acme_mcp: { api_key: 'sk-install-abcdef' } },
|
|
54
|
+
});
|
|
55
|
+
expect(result.valid).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('rejects a whitespace-only key on the install path too', () => {
|
|
59
|
+
const result = validateApiRequest('AppInstallationPayload', {
|
|
60
|
+
app_id: APP_ID,
|
|
61
|
+
api_key_params: { acme_mcp: { api_key: ' ' } },
|
|
62
|
+
});
|
|
63
|
+
expect(result.valid).toBe(false);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('stays optional — installs with no api_key collections send nothing', () => {
|
|
67
|
+
expect(validateApiRequest('AppInstallationPayload', { app_id: APP_ID }).valid).toBe(true);
|
|
68
|
+
expect(AppInstallationPayloadSchema.parse({ app_id: APP_ID }).api_key_params).toBeUndefined();
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('McpApiKeyStatus', () => {
|
|
73
|
+
it('publishes hint as required-but-nullable, not optional', () => {
|
|
74
|
+
// Every response sets `hint`, so the field must not be optional in the generated clients.
|
|
75
|
+
assertType<Equals<McpApiKeyStatus, { configured: boolean; hint: string | null }>>(true);
|
|
76
|
+
expect(McpApiKeyStatusSchema.safeParse({ configured: false }).success).toBe(false);
|
|
77
|
+
expect(McpApiKeyStatusSchema.safeParse({ configured: false, hint: null }).success).toBe(true);
|
|
78
|
+
expect(McpApiKeyStatusSchema.safeParse({ configured: true, hint: 'sk-l...3456' }).success).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('never carries the key itself', () => {
|
|
82
|
+
expect(McpApiKeyStatusSchema.safeParse({ configured: true, hint: null, api_key: 'sk-live' }).success).toBe(
|
|
83
|
+
false,
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -80,7 +80,7 @@ import {
|
|
|
80
80
|
SystemRoleDefinitionArraySchema,
|
|
81
81
|
} from './access-control.js';
|
|
82
82
|
import { AccountSchema, StripeBillingStatusResponseSchema, UpdateAccountPayloadSchema } from './account.js';
|
|
83
|
-
import { findUnprunablePaths, type JsonObject, pruneToSchema, toOpenApiComponents } from './adapter.js';
|
|
83
|
+
import { findUnprunablePaths, isPlainObject, type JsonObject, pruneToSchema, toOpenApiComponents } from './adapter.js';
|
|
84
84
|
import * as AgentRunSchemas from './agent-runs.js';
|
|
85
85
|
import {
|
|
86
86
|
AnalyticsAxisSchema,
|
|
@@ -109,9 +109,11 @@ import {
|
|
|
109
109
|
AgentRunTypeSchema,
|
|
110
110
|
AgentToolApprovalClassSchema,
|
|
111
111
|
AgentToolDefinitionSchema,
|
|
112
|
+
AppApiKeyCollectionParamsSchema,
|
|
112
113
|
AppBuildProgressSchema,
|
|
113
114
|
AppBuildProgressStatusSchema,
|
|
114
115
|
AppBuildTriggerSchema,
|
|
116
|
+
AppDeleteSummarySchema,
|
|
115
117
|
AppDevelopmentTaskDetailsSchema,
|
|
116
118
|
AppDevelopmentTaskListSchema,
|
|
117
119
|
AppDevelopmentTaskSchema,
|
|
@@ -155,6 +157,7 @@ import {
|
|
|
155
157
|
EventRefSchema,
|
|
156
158
|
Extract_AppVersionGitRefType_branch_tag_commitSchema,
|
|
157
159
|
InCodeTypeRefSchema,
|
|
160
|
+
McpApiKeyCredentialSchema,
|
|
158
161
|
OAuthClientCredentialsMapSchema,
|
|
159
162
|
OAuthClientCredentialsSchema,
|
|
160
163
|
RunKindSchema,
|
|
@@ -178,6 +181,7 @@ import {
|
|
|
178
181
|
AppSourceConfigSchema,
|
|
179
182
|
AppUIConfigSchema,
|
|
180
183
|
MCPToolAnnotationsSchema,
|
|
184
|
+
McpApiKeyStatusSchema,
|
|
181
185
|
McpOAuthConnectResponseSchema,
|
|
182
186
|
McpOAuthDisconnectResponseSchema,
|
|
183
187
|
McpOAuthTokenRequestSchema,
|
|
@@ -186,6 +190,7 @@ import {
|
|
|
186
190
|
OAuthAuthStatusArraySchema,
|
|
187
191
|
OAuthAuthStatusSchema,
|
|
188
192
|
OAuthMetadataResponseSchema,
|
|
193
|
+
SetMcpApiKeyRequestSchema,
|
|
189
194
|
ToolCollectionObjectSchema,
|
|
190
195
|
} from './apps.js';
|
|
191
196
|
import {
|
|
@@ -1585,17 +1590,21 @@ const EVENT_DELIVERY_SCHEMAS = {
|
|
|
1585
1590
|
EventOutboxStatus: EventSchemas.EventOutboxStatusSchema,
|
|
1586
1591
|
EventDeliverySortField: EventSchemas.EventDeliverySortFieldSchema,
|
|
1587
1592
|
WebhookEventDeliveryTarget: EventSchemas.WebhookEventDeliveryTargetSchema,
|
|
1593
|
+
AppEventDeliveryTarget: EventSchemas.AppEventDeliveryTargetSchema,
|
|
1588
1594
|
WorkflowEventDeliveryTarget: EventSchemas.WorkflowEventDeliveryTargetSchema,
|
|
1589
1595
|
EventDeliveryQueueFailureSummary: EventSchemas.EventDeliveryQueueFailureSummarySchema,
|
|
1590
1596
|
EventOutboxQueueSummary: EventSchemas.EventOutboxQueueSummarySchema,
|
|
1591
1597
|
EventDeliveryQueueSortField: EventSchemas.EventDeliveryQueueSortFieldSchema,
|
|
1592
1598
|
AgentEventDeliveryTarget: EventSchemas.AgentEventDeliveryTargetSchema,
|
|
1593
1599
|
WebhookEventDeliveryTargetInput: EventSchemas.WebhookEventDeliveryTargetInputSchema,
|
|
1600
|
+
AppEventDeliveryTargetInput: EventSchemas.AppEventDeliveryTargetInputSchema,
|
|
1594
1601
|
WorkflowEventDeliveryTargetInput: EventSchemas.WorkflowEventDeliveryTargetInputSchema,
|
|
1595
1602
|
SemanticEvaluator: EventSchemas.SemanticEvaluatorSchema,
|
|
1596
1603
|
SemanticEvaluationRecord: EventSchemas.SemanticEvaluationRecordSchema,
|
|
1597
1604
|
EventDeliveryQueueSubscriptionSummary: EventSchemas.EventDeliveryQueueSubscriptionSummarySchema,
|
|
1598
1605
|
EventDeliveryQueueSummaryPayload: EventSchemas.EventDeliveryQueueSummaryPayloadSchema,
|
|
1606
|
+
CancelEventDeliveryIntentsPayload: EventSchemas.CancelEventDeliveryIntentsPayloadSchema,
|
|
1607
|
+
CancelEventDeliveryIntentsResponse: EventSchemas.CancelEventDeliveryIntentsResponseSchema,
|
|
1599
1608
|
EventSemanticCondition: EventSchemas.EventSemanticConditionSchema,
|
|
1600
1609
|
EventDeliveryIntentSummary: EventSchemas.EventDeliveryIntentSummarySchema,
|
|
1601
1610
|
EventDeliveryQueueSummaryResponse: EventSchemas.EventDeliveryQueueSummaryResponseSchema,
|
|
@@ -1806,6 +1815,10 @@ const REMOTE_MCP_SCHEMAS = {
|
|
|
1806
1815
|
McpOAuthConnectResponse: McpOAuthConnectResponseSchema,
|
|
1807
1816
|
OAuthAuthorizeResponse: OAuthAuthorizeResponseSchema,
|
|
1808
1817
|
OAuthAuthStatusArray: OAuthAuthStatusArraySchema,
|
|
1818
|
+
// The static-key alternative to that handshake: the key itself is write-only, so only the
|
|
1819
|
+
// setter payload and the configured/hint projection are published.
|
|
1820
|
+
SetMcpApiKeyRequest: SetMcpApiKeyRequestSchema,
|
|
1821
|
+
McpApiKeyStatus: McpApiKeyStatusSchema,
|
|
1809
1822
|
} as const satisfies Record<string, z.ZodType>;
|
|
1810
1823
|
|
|
1811
1824
|
const AUDIT_TRAIL_SCHEMAS = {
|
|
@@ -1919,6 +1932,7 @@ const APP_LIFECYCLE_SCHEMAS = {
|
|
|
1919
1932
|
RunType: RunTypeSchema,
|
|
1920
1933
|
AppBuildProgressStatus: AppBuildProgressStatusSchema,
|
|
1921
1934
|
DeleteAppVersionResponse: DeleteAppVersionResponseSchema,
|
|
1935
|
+
AppDeleteSummary: AppDeleteSummarySchema,
|
|
1922
1936
|
AppRepoBranch: AppRepoBranchSchema,
|
|
1923
1937
|
AppRepoDocumentCommit: AppRepoDocumentCommitSchema,
|
|
1924
1938
|
AppVersionGitSource: AppVersionGitSourceSchema,
|
|
@@ -1928,6 +1942,8 @@ const APP_LIFECYCLE_SCHEMAS = {
|
|
|
1928
1942
|
AppDevelopmentTaskList: AppDevelopmentTaskListSchema,
|
|
1929
1943
|
OAuthClientCredentialsMap: OAuthClientCredentialsMapSchema,
|
|
1930
1944
|
AppOAuthCollectionParams: AppOAuthCollectionParamsSchema,
|
|
1945
|
+
McpApiKeyCredential: McpApiKeyCredentialSchema,
|
|
1946
|
+
AppApiKeyCollectionParams: AppApiKeyCollectionParamsSchema,
|
|
1931
1947
|
AppInspectionIssue: AppInspectionIssueSchema,
|
|
1932
1948
|
AppScaffoldProgress: AppScaffoldProgressSchema,
|
|
1933
1949
|
AppRepoTree: AppRepoTreeSchema,
|
|
@@ -2077,6 +2093,7 @@ const STS_SCHEMAS = {
|
|
|
2077
2093
|
ServiceAccountTokenRequest: StsSchemas.ServiceAccountTokenRequestSchema,
|
|
2078
2094
|
IssueTokenRequest: StsSchemas.IssueTokenRequestSchema,
|
|
2079
2095
|
IssueTokenResponse: StsSchemas.IssueTokenResponseSchema,
|
|
2096
|
+
IssueTokenForbiddenResponse: StsSchemas.IssueTokenForbiddenResponseSchema,
|
|
2080
2097
|
IssueTokenUnavailableResponse: StsSchemas.IssueTokenUnavailableResponseSchema,
|
|
2081
2098
|
} as const satisfies Record<string, z.ZodType>;
|
|
2082
2099
|
|
|
@@ -2224,6 +2241,7 @@ const STRICT_COMPONENTS: ReadonlySet<string> = new Set<string>([
|
|
|
2224
2241
|
'AgentTokenRequest',
|
|
2225
2242
|
'ServiceAccountTokenRequest',
|
|
2226
2243
|
'IssueTokenResponse',
|
|
2244
|
+
'IssueTokenForbiddenResponse',
|
|
2227
2245
|
'IssueTokenUnavailableResponse',
|
|
2228
2246
|
'ApiKeyListQuery',
|
|
2229
2247
|
// These payloads previously referenced closed generated utility components. They now own their
|
|
@@ -2371,6 +2389,7 @@ const STRICT_COMPONENTS: ReadonlySet<string> = new Set<string>([
|
|
|
2371
2389
|
'AppUIConfig',
|
|
2372
2390
|
'AppUINavItem',
|
|
2373
2391
|
'MCPOAuthConfig',
|
|
2392
|
+
'MCPApiKeyConfig',
|
|
2374
2393
|
'MCPToolCollectionObject',
|
|
2375
2394
|
'VertesiaSDKToolCollectionObject',
|
|
2376
2395
|
// File, task, content-type and command components. Every one is published closed
|
|
@@ -2662,6 +2681,8 @@ const STRICT_COMPONENTS: ReadonlySet<string> = new Set<string>([
|
|
|
2662
2681
|
'ListWorkflowRunsResponse',
|
|
2663
2682
|
'EventDeliveryQueueSubscriptionSummary',
|
|
2664
2683
|
'EventDeliveryQueueSummaryPayload',
|
|
2684
|
+
'CancelEventDeliveryIntentsPayload',
|
|
2685
|
+
'CancelEventDeliveryIntentsResponse',
|
|
2665
2686
|
'ContentObjectExportStatusResponse',
|
|
2666
2687
|
'TimerTask',
|
|
2667
2688
|
'SignalTask',
|
|
@@ -2929,6 +2950,8 @@ const STRICT_COMPONENTS: ReadonlySet<string> = new Set<string>([
|
|
|
2929
2950
|
'McpOAuthDisconnectResponse',
|
|
2930
2951
|
'McpOAuthConnectResponse',
|
|
2931
2952
|
'OAuthAuthorizeResponse',
|
|
2953
|
+
'SetMcpApiKeyRequest',
|
|
2954
|
+
'McpApiKeyStatus',
|
|
2932
2955
|
'AuditMeter',
|
|
2933
2956
|
'AuditAggregationDimensionMap',
|
|
2934
2957
|
'AuditAggregationRow',
|
|
@@ -2982,6 +3005,7 @@ const STRICT_COMPONENTS: ReadonlySet<string> = new Set<string>([
|
|
|
2982
3005
|
'AppInstallationProviderBinding',
|
|
2983
3006
|
'AppInstallationOAuthBinding',
|
|
2984
3007
|
'OAuthClientCredentials',
|
|
3008
|
+
'McpApiKeyCredential',
|
|
2985
3009
|
'AppInspectionCapabilityReport',
|
|
2986
3010
|
'AppRepoTreeEntry',
|
|
2987
3011
|
'AppRepoRef',
|
|
@@ -2990,6 +3014,7 @@ const STRICT_COMPONENTS: ReadonlySet<string> = new Set<string>([
|
|
|
2990
3014
|
'InCodeTypeRef',
|
|
2991
3015
|
'StoredTypeRef',
|
|
2992
3016
|
'DeleteAppVersionResponse',
|
|
3017
|
+
'AppDeleteSummary',
|
|
2993
3018
|
'AppRepoBranch',
|
|
2994
3019
|
'AppRepoDocumentCommit',
|
|
2995
3020
|
'AppVersionGitSource',
|
|
@@ -3331,14 +3356,88 @@ const validators = new Map<string, ValidateFunction>();
|
|
|
3331
3356
|
*/
|
|
3332
3357
|
let ajvInstance: Ajv2020 | undefined;
|
|
3333
3358
|
|
|
3359
|
+
/**
|
|
3360
|
+
* The published components as AJV can consume them: the same schemas, with the two adjustments
|
|
3361
|
+
* AJV's discriminator support requires and the OpenAPI document must not have.
|
|
3362
|
+
*
|
|
3363
|
+
* `discriminator: true` below is what makes AJV validate a discriminated union through the branch
|
|
3364
|
+
* its tag names, instead of trying every branch and reporting all of their failures at once. The
|
|
3365
|
+
* difference is not cosmetic — a legacy MCP tool collection missing `id` used to report the real
|
|
3366
|
+
* error alongside two impossible ones from the branch it was never meant to match:
|
|
3367
|
+
*
|
|
3368
|
+
* / must have required property 'id' <- the real one
|
|
3369
|
+
* / must NOT have additional properties: oauth_app <- vertesia_sdk branch
|
|
3370
|
+
* /type must be equal to constant <- vertesia_sdk branch
|
|
3371
|
+
* / must match exactly one schema in oneOf
|
|
3372
|
+
*
|
|
3373
|
+
* AJV rejects `mapping` outright ("discriminator: mapping is not supported"), because it derives
|
|
3374
|
+
* the tag-to-branch map from each branch's own `const`/`enum` instead. The OpenAPI document still
|
|
3375
|
+
* needs the mapping — a generated Java or Go client reads it to pick a concrete subtype — so both
|
|
3376
|
+
* fixups here apply to AJV's copy only, and `ApiSchemaComponents` is published exactly as built.
|
|
3377
|
+
*
|
|
3378
|
+
* AJV's remaining requirements — the tag required in every branch, carrying `const` or a
|
|
3379
|
+
* single-valued `enum`, with the union node typed as an object — are what `synthesizeDiscriminator`
|
|
3380
|
+
* in the adapter already checks before it emits a discriminator at all, and what the hand-written
|
|
3381
|
+
* `.meta({ discriminator })` declarations restate. `api-discriminators.test.ts` compiles every
|
|
3382
|
+
* registered component so a union satisfying neither fails the build rather than the request.
|
|
3383
|
+
*/
|
|
3384
|
+
function toAjvComponents(): Record<string, JsonObject> {
|
|
3385
|
+
const schemas = structuredClone(ApiSchemaComponents) as Record<string, JsonObject>;
|
|
3386
|
+
const resolve = (value: unknown): JsonObject | undefined => {
|
|
3387
|
+
if (!isPlainObject(value)) return undefined;
|
|
3388
|
+
const ref = value.$ref;
|
|
3389
|
+
if (typeof ref !== 'string' || !ref.startsWith(COMPONENT_REF_PREFIX)) return value;
|
|
3390
|
+
return schemas[ref.slice(COMPONENT_REF_PREFIX.length)];
|
|
3391
|
+
};
|
|
3392
|
+
|
|
3393
|
+
visitSchemaNodes(schemas, (node) => {
|
|
3394
|
+
const discriminator = node.discriminator;
|
|
3395
|
+
if (!isPlainObject(discriminator) || !Array.isArray(node.oneOf)) return;
|
|
3396
|
+
// AJV derives the tag-to-branch map from each branch's own literal, so `mapping` is both
|
|
3397
|
+
// redundant and rejected.
|
|
3398
|
+
delete discriminator.mapping;
|
|
3399
|
+
|
|
3400
|
+
const tag = discriminator.propertyName;
|
|
3401
|
+
if (typeof tag !== 'string') return;
|
|
3402
|
+
for (const member of node.oneOf) {
|
|
3403
|
+
const branch = resolve(member);
|
|
3404
|
+
const properties = branch && isPlainObject(branch.properties) ? branch.properties : undefined;
|
|
3405
|
+
const tagSchema = properties?.[tag];
|
|
3406
|
+
if (!isPlainObject(tagSchema) || tagSchema.const !== undefined || tagSchema.enum !== undefined) continue;
|
|
3407
|
+
// The tag is a `$ref` to a named literal component — `TaskType_ACTIVITY`,
|
|
3408
|
+
// `SupportedIntegrations_gladia` — because the literal carries its own `.meta({ id })`.
|
|
3409
|
+
// AJV reads `properties/<tag>` looking for `const` or `enum` and does not follow a
|
|
3410
|
+
// `$ref` to find one, so it refuses to compile the union at all. Restating the resolved
|
|
3411
|
+
// literal ALONGSIDE the `$ref` is validation-neutral: `$ref` has no special precedence
|
|
3412
|
+
// in 2020-12, both keywords apply, and they carry the same value by construction.
|
|
3413
|
+
const target = resolve(tagSchema);
|
|
3414
|
+
if (!target) continue;
|
|
3415
|
+
if (target.const !== undefined) tagSchema.const = target.const;
|
|
3416
|
+
else if (Array.isArray(target.enum)) tagSchema.enum = target.enum;
|
|
3417
|
+
}
|
|
3418
|
+
});
|
|
3419
|
+
return schemas;
|
|
3420
|
+
}
|
|
3421
|
+
|
|
3422
|
+
/** Every object node in the component graph, parents before children. */
|
|
3423
|
+
function visitSchemaNodes(value: unknown, visit: (node: JsonObject) => void): void {
|
|
3424
|
+
if (Array.isArray(value)) {
|
|
3425
|
+
for (const item of value) visitSchemaNodes(item, visit);
|
|
3426
|
+
return;
|
|
3427
|
+
}
|
|
3428
|
+
if (!isPlainObject(value)) return;
|
|
3429
|
+
visit(value);
|
|
3430
|
+
for (const item of Object.values(value)) visitSchemaNodes(item, visit);
|
|
3431
|
+
}
|
|
3432
|
+
|
|
3334
3433
|
function getAjv(): Ajv2020 {
|
|
3335
3434
|
if (ajvInstance) return ajvInstance;
|
|
3336
|
-
const ajv = new Ajv2020({ strictSchema: false, allErrors: true });
|
|
3435
|
+
const ajv = new Ajv2020({ strictSchema: false, allErrors: true, discriminator: true });
|
|
3337
3436
|
// Without this, AJV treats `format` as an annotation and ignores it, so a `date-time` property
|
|
3338
3437
|
// would document a constraint nothing checks — the exact spec/enforcement gap this design is
|
|
3339
3438
|
// meant to close.
|
|
3340
3439
|
addFormats(ajv);
|
|
3341
|
-
ajv.addSchema({ $id: 'vertesia://openapi', components: { schemas:
|
|
3440
|
+
ajv.addSchema({ $id: 'vertesia://openapi', components: { schemas: toAjvComponents() } });
|
|
3342
3441
|
ajvInstance = ajv;
|
|
3343
3442
|
return ajv;
|
|
3344
3443
|
}
|
|
@@ -3351,20 +3450,101 @@ function getValidator(name: ApiComponentName): ValidateFunction {
|
|
|
3351
3450
|
return validate;
|
|
3352
3451
|
}
|
|
3353
3452
|
|
|
3354
|
-
function
|
|
3355
|
-
|
|
3453
|
+
function additionalProperty(error: { params?: unknown }): string | undefined {
|
|
3454
|
+
// AJV writes "must NOT have additional properties" and puts the offending key in `params`,
|
|
3455
|
+
// so the message alone says a body is wrong without saying which property made it wrong.
|
|
3456
|
+
// Every other keyword names its subject already — `required` quotes the missing property,
|
|
3457
|
+
// `enum` and `type` describe the value in place — and this is the one a caller most often
|
|
3458
|
+
// trips, so it is the one worth spelling out rather than reformatting all of them.
|
|
3459
|
+
return (error.params as { additionalProperty?: string } | undefined)?.additionalProperty;
|
|
3460
|
+
}
|
|
3461
|
+
|
|
3462
|
+
/**
|
|
3463
|
+
* One validation failure, with the undeclared property names kept AS NAMES.
|
|
3464
|
+
*
|
|
3465
|
+
* The structure exists because the rendered line cannot be taken apart again. Property names are
|
|
3466
|
+
* arbitrary JSON strings: `{"customer secret token": 1}` and `{"a, b": 1}` are both valid, so
|
|
3467
|
+
* neither a space nor a comma is a reliable separator once the names have been joined. Any consumer
|
|
3468
|
+
* that needs to shorten the list — the HTTP boundary does, the log does not — has to do it here,
|
|
3469
|
+
* on the array, where a name can be dropped whole.
|
|
3470
|
+
*/
|
|
3471
|
+
export interface ApiValidationIssue {
|
|
3472
|
+
/**
|
|
3473
|
+
* AJV's `instancePath` for the failing value, with `'/'` substituted for the root.
|
|
3474
|
+
*
|
|
3475
|
+
* Not quite a JSON Pointer: the pointer for a document root is the empty string, which reads as
|
|
3476
|
+
* a missing path in a message. `'/'` is a display sentinel, so treat this as human-facing rather
|
|
3477
|
+
* than as something to resolve against the payload.
|
|
3478
|
+
*/
|
|
3479
|
+
path: string;
|
|
3480
|
+
/** AJV's own message, e.g. `must NOT have additional properties`. */
|
|
3481
|
+
message: string;
|
|
3482
|
+
/** Undeclared property names at {@link path}, in the order AJV reported them. */
|
|
3483
|
+
undeclared?: string[];
|
|
3484
|
+
}
|
|
3485
|
+
|
|
3486
|
+
/**
|
|
3487
|
+
* The failures, with all undeclared properties at a given path gathered into ONE issue.
|
|
3488
|
+
*
|
|
3489
|
+
* AJV reports `additionalProperties` per property, so a value carrying a whole foreign object —
|
|
3490
|
+
* a Mongoose document that reached the response mapper unmapped, say — produces one error per own
|
|
3491
|
+
* key and buries every other failure in the payload. Gathering is lossless: every name is kept, in
|
|
3492
|
+
* the order AJV found it, on the issue for the path it belongs to.
|
|
3493
|
+
*/
|
|
3494
|
+
function collectIssues(validate: ValidateFunction): ApiValidationIssue[] {
|
|
3495
|
+
const errors = validate.errors ?? [];
|
|
3496
|
+
const undeclaredByPath = new Map<string, Set<string>>();
|
|
3497
|
+
for (const error of errors) {
|
|
3498
|
+
const additional = additionalProperty(error);
|
|
3499
|
+
if (additional === undefined) continue;
|
|
3356
3500
|
const where = error.instancePath || '/';
|
|
3357
|
-
|
|
3358
|
-
//
|
|
3359
|
-
//
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3501
|
+
const names = undeclaredByPath.get(where);
|
|
3502
|
+
// A union whose branches all reject the same property reports it once per branch. The name
|
|
3503
|
+
// is the same fact each time, so it is listed once.
|
|
3504
|
+
if (names) names.add(additional);
|
|
3505
|
+
else undeclaredByPath.set(where, new Set([additional]));
|
|
3506
|
+
}
|
|
3507
|
+
|
|
3508
|
+
const gathered = new Set<string>();
|
|
3509
|
+
const issues: ApiValidationIssue[] = [];
|
|
3510
|
+
for (const error of errors) {
|
|
3511
|
+
const path = error.instancePath || '/';
|
|
3512
|
+
const message = error.message ?? 'is invalid';
|
|
3513
|
+
if (additionalProperty(error) === undefined) {
|
|
3514
|
+
issues.push({ path, message });
|
|
3515
|
+
continue;
|
|
3516
|
+
}
|
|
3517
|
+
if (gathered.has(path)) continue;
|
|
3518
|
+
gathered.add(path);
|
|
3519
|
+
issues.push({ path, message, undeclared: [...(undeclaredByPath.get(path) ?? [])] });
|
|
3520
|
+
}
|
|
3521
|
+
return issues;
|
|
3522
|
+
}
|
|
3523
|
+
|
|
3524
|
+
/**
|
|
3525
|
+
* The complete rendering of one issue — every name, no budget.
|
|
3526
|
+
*
|
|
3527
|
+
* This is what gets logged. A caller-facing message renders from {@link ApiValidationIssue} itself
|
|
3528
|
+
* with a length limit rather than shortening this string; see `reportableErrors` in the enforcer.
|
|
3529
|
+
*/
|
|
3530
|
+
export function renderApiValidationIssue(issue: ApiValidationIssue): string {
|
|
3531
|
+
const head = `${issue.path} ${issue.message}`;
|
|
3532
|
+
return issue.undeclared?.length ? `${head}: ${issue.undeclared.join(', ')}` : head;
|
|
3533
|
+
}
|
|
3534
|
+
|
|
3535
|
+
/** The failed branch of every validator here, so the two views cannot be built inconsistently. */
|
|
3536
|
+
function invalidResult(validate: ValidateFunction): { valid: false; errors: string[]; issues: ApiValidationIssue[] } {
|
|
3537
|
+
const issues = collectIssues(validate);
|
|
3538
|
+
return { valid: false, errors: issues.map(renderApiValidationIssue), issues };
|
|
3365
3539
|
}
|
|
3366
3540
|
|
|
3367
|
-
export type ValidateApiPayloadResult<T> =
|
|
3541
|
+
export type ValidateApiPayloadResult<T> =
|
|
3542
|
+
| { valid: true; data: T }
|
|
3543
|
+
/**
|
|
3544
|
+
* `errors` is the rendered form, complete and ready to log; `issues` is the same failures with
|
|
3545
|
+
* the property names still separable. Shorten from `issues`, never from `errors`.
|
|
3546
|
+
*/
|
|
3547
|
+
| { valid: false; errors: string[]; issues: ApiValidationIssue[] };
|
|
3368
3548
|
|
|
3369
3549
|
/**
|
|
3370
3550
|
* Validates an untyped request body against the component the endpoint publishes.
|
|
@@ -3386,7 +3566,7 @@ export function validateApiRequest<N extends ApiComponentName>(
|
|
|
3386
3566
|
if (validate(value)) {
|
|
3387
3567
|
return { valid: true, data: value as ApiComponentType<N> };
|
|
3388
3568
|
}
|
|
3389
|
-
return
|
|
3569
|
+
return invalidResult(validate);
|
|
3390
3570
|
}
|
|
3391
3571
|
|
|
3392
3572
|
/**
|
|
@@ -3404,7 +3584,7 @@ export function validateApiResponse<N extends ApiComponentName>(
|
|
|
3404
3584
|
if (validate(value)) {
|
|
3405
3585
|
return { valid: true, data: value as ApiComponentType<N> };
|
|
3406
3586
|
}
|
|
3407
|
-
return
|
|
3587
|
+
return invalidResult(validate);
|
|
3408
3588
|
}
|
|
3409
3589
|
|
|
3410
3590
|
/**
|
|
@@ -3432,7 +3612,9 @@ export function pruneApiResponse<N extends ApiComponentName>(name: N, value: Api
|
|
|
3432
3612
|
return pruneToSchema(value, { $ref: apiComponentRef(name) }, ApiSchemaComponents) as ApiComponentType<N>;
|
|
3433
3613
|
}
|
|
3434
3614
|
|
|
3435
|
-
export type PruneAndValidateResult<T> =
|
|
3615
|
+
export type PruneAndValidateResult<T> =
|
|
3616
|
+
| { valid: true; data: T }
|
|
3617
|
+
| { valid: false; data: unknown; errors: string[]; issues: ApiValidationIssue[] };
|
|
3436
3618
|
|
|
3437
3619
|
/**
|
|
3438
3620
|
* Prunes an untyped payload and validates the result against the published component.
|
|
@@ -3453,7 +3635,7 @@ export function pruneAndValidateApiResponse<N extends ApiComponentName>(
|
|
|
3453
3635
|
if (validate(pruned)) {
|
|
3454
3636
|
return { valid: true, data: pruned as ApiComponentType<N> };
|
|
3455
3637
|
}
|
|
3456
|
-
return {
|
|
3638
|
+
return { ...invalidResult(validate), data: pruned };
|
|
3457
3639
|
}
|
|
3458
3640
|
|
|
3459
3641
|
/**
|
|
@@ -131,6 +131,27 @@ describe.skipIf(!publishedDocument)('gate — generated artifact = canonical com
|
|
|
131
131
|
});
|
|
132
132
|
});
|
|
133
133
|
|
|
134
|
+
describe('content object response classification', () => {
|
|
135
|
+
it('accepts the current sensitivity and compartments fields', () => {
|
|
136
|
+
const result = validateApiResponse('ContentObjectApiResponse', {
|
|
137
|
+
id: 'content-object-id',
|
|
138
|
+
name: 'Classified document',
|
|
139
|
+
updated_by: 'user-id',
|
|
140
|
+
created_by: 'user-id',
|
|
141
|
+
created_at: '2026-08-06T09:00:00.000Z',
|
|
142
|
+
updated_at: '2026-08-06T09:00:00.000Z',
|
|
143
|
+
location: '/',
|
|
144
|
+
status: 'completed',
|
|
145
|
+
properties: {},
|
|
146
|
+
revision: { root: 'content-object-id', head: true },
|
|
147
|
+
sensitivity: 3,
|
|
148
|
+
compartments: ['legal'],
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
expect(result).toMatchObject({ valid: true });
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
134
155
|
/**
|
|
135
156
|
* Bodies that probe where the two validators could disagree.
|
|
136
157
|
*
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
AgentTokenRequest,
|
|
5
5
|
ApiKeyTokenRequest,
|
|
6
6
|
EnvironmentTokenRequest,
|
|
7
|
+
IssueTokenForbiddenResponse,
|
|
7
8
|
IssueTokenRequest,
|
|
8
9
|
IssueTokenResponse,
|
|
9
10
|
ProjectTokenRequest,
|
|
@@ -16,6 +17,7 @@ import type {
|
|
|
16
17
|
AgentTokenRequestSchema,
|
|
17
18
|
ApiKeyTokenRequestSchema,
|
|
18
19
|
EnvironmentTokenRequestSchema,
|
|
20
|
+
IssueTokenForbiddenResponseSchema,
|
|
19
21
|
IssueTokenRequestSchema,
|
|
20
22
|
IssueTokenResponseSchema,
|
|
21
23
|
ProjectTokenRequestSchema,
|
|
@@ -38,6 +40,7 @@ describe('STS canonical contracts', () => {
|
|
|
38
40
|
assertType<Equals<ServiceAccountTokenRequest, z.infer<typeof ServiceAccountTokenRequestSchema>>>(true);
|
|
39
41
|
assertType<Equals<IssueTokenRequest, z.infer<typeof IssueTokenRequestSchema>>>(true);
|
|
40
42
|
assertType<Equals<IssueTokenResponse, z.infer<typeof IssueTokenResponseSchema>>>(true);
|
|
43
|
+
assertType<Equals<IssueTokenForbiddenResponse, z.infer<typeof IssueTokenForbiddenResponseSchema>>>(true);
|
|
41
44
|
expect(true).toBe(true);
|
|
42
45
|
});
|
|
43
46
|
|
|
@@ -60,9 +63,38 @@ describe('STS canonical contracts', () => {
|
|
|
60
63
|
expect(validateApiRequest('IssueTokenRequest', { type: 'project', account_id: 'acc_1' }).valid).toBe(false);
|
|
61
64
|
});
|
|
62
65
|
|
|
63
|
-
it('checks
|
|
66
|
+
it('checks every documented response shape and forbidden error-code literal', () => {
|
|
64
67
|
expect(validateApiResponse('IssueTokenResponse', { token: 'signed', token_type: 'Bearer' }).valid).toBe(true);
|
|
65
68
|
expect(validateApiResponse('IssueTokenResponse', { token: 'signed', token_type: 'bearer' }).valid).toBe(false);
|
|
69
|
+
for (const errorCode of [
|
|
70
|
+
'requested_scope_unavailable',
|
|
71
|
+
'no_accessible_account',
|
|
72
|
+
'restricted_environment',
|
|
73
|
+
'token_issuance_forbidden',
|
|
74
|
+
]) {
|
|
75
|
+
expect(
|
|
76
|
+
validateApiResponse('IssueTokenForbiddenResponse', {
|
|
77
|
+
error: 'Forbidden',
|
|
78
|
+
message: 'The requested scope is unavailable',
|
|
79
|
+
errorCode,
|
|
80
|
+
}).valid,
|
|
81
|
+
).toBe(true);
|
|
82
|
+
}
|
|
83
|
+
expect(
|
|
84
|
+
validateApiResponse('IssueTokenForbiddenResponse', {
|
|
85
|
+
error: 'Forbidden',
|
|
86
|
+
message: 'No',
|
|
87
|
+
errorCode: 'resource_not_found',
|
|
88
|
+
}).valid,
|
|
89
|
+
).toBe(false);
|
|
90
|
+
expect(
|
|
91
|
+
validateApiResponse('IssueTokenForbiddenResponse', {
|
|
92
|
+
error: 'Forbidden',
|
|
93
|
+
message: 'No',
|
|
94
|
+
errorCode: 'requested_scope_unavailable',
|
|
95
|
+
project_id: 'secret',
|
|
96
|
+
}).valid,
|
|
97
|
+
).toBe(false);
|
|
66
98
|
expect(
|
|
67
99
|
validateApiResponse('IssueTokenUnavailableResponse', {
|
|
68
100
|
error: 'Service Unavailable',
|
package/src/api-schemas/sts.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { ISSUE_TOKEN_FORBIDDEN_ERROR_CODES } from '../sts-errors.js';
|
|
2
3
|
import { SystemRolesSchema } from './apikey.js';
|
|
3
4
|
|
|
4
5
|
const ALGORITHM_DESCRIPTION = 'Signing algorithm - defaults to ES256. Use RS256 for Azure AD compatibility.';
|
|
@@ -105,6 +106,18 @@ export const IssueTokenResponseSchema = z
|
|
|
105
106
|
})
|
|
106
107
|
.meta({ id: 'IssueTokenResponse' });
|
|
107
108
|
|
|
109
|
+
export const IssueTokenForbiddenResponseSchema = z
|
|
110
|
+
.strictObject({
|
|
111
|
+
error: z.string(),
|
|
112
|
+
message: z.string(),
|
|
113
|
+
errorCode: z.enum(ISSUE_TOKEN_FORBIDDEN_ERROR_CODES),
|
|
114
|
+
})
|
|
115
|
+
.meta({
|
|
116
|
+
id: 'IssueTokenForbiddenResponse',
|
|
117
|
+
description:
|
|
118
|
+
'A safe, machine-readable denial response. Requested resources are intentionally not identified or confirmed.',
|
|
119
|
+
});
|
|
120
|
+
|
|
108
121
|
export const IssueTokenUnavailableResponseSchema = z
|
|
109
122
|
.strictObject({ error: z.string(), message: z.string() })
|
|
110
123
|
.meta({ id: 'IssueTokenUnavailableResponse' });
|