@voyant-travel/apps 0.6.0 → 0.6.2
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/api-runtime.d.ts +2 -2
- package/dist/routes-openapi.d.ts +859 -0
- package/dist/routes-openapi.js +195 -0
- package/dist/routes.d.ts +2 -2
- package/dist/routes.js +26 -25
- package/dist/runtime-contributor.d.ts +1 -0
- package/dist/runtime-contributor.js +3 -0
- package/dist/voyant.js +93 -9
- package/openapi/admin/apps.json +72 -0
- package/package.json +16 -9
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { createRoute, z } from "@hono/zod-openapi";
|
|
2
|
+
import { activateInstallationBodySchema, appCredentialRevocationSchema, appInstallationAuditQuerySchema, appInstallationListQuerySchema, appListQuerySchema, appOAuthAuthorizeQuerySchema, appOAuthTokenSchema, appSessionTokenExchangeSchema, appSessionTokenIssueSchema, appWebhookReplaySchema, createCustomAppRegistrationSchema, installAppSchema, lifecycleActionBodySchema, releaseManifestFetchSchema, releaseManifestUploadSchema, } from "./contracts.js";
|
|
3
|
+
const appIdParamSchema = z.object({ appId: z.string().min(1) });
|
|
4
|
+
const installationIdParamSchema = z.object({ installationId: z.string().min(1) });
|
|
5
|
+
const errorSchema = z.object({ error: z.string() });
|
|
6
|
+
const dataEnvelopeSchema = z.object({ data: z.unknown() });
|
|
7
|
+
const listEnvelopeSchema = z.object({
|
|
8
|
+
data: z.array(z.unknown()),
|
|
9
|
+
total: z.number(),
|
|
10
|
+
limit: z.number(),
|
|
11
|
+
offset: z.number(),
|
|
12
|
+
});
|
|
13
|
+
const releaseCreateResponseSchema = z.object({
|
|
14
|
+
data: z.unknown(),
|
|
15
|
+
digest: z.string(),
|
|
16
|
+
created: z.boolean(),
|
|
17
|
+
});
|
|
18
|
+
const oauthRedirectResponseSchema = z.object({
|
|
19
|
+
data: z.object({ redirectUrl: z.string().url(), state: z.string() }),
|
|
20
|
+
});
|
|
21
|
+
const tokenResponseSchema = z.record(z.string(), z.unknown());
|
|
22
|
+
const credentialRevocationResponseSchema = z.object({
|
|
23
|
+
installationId: z.string(),
|
|
24
|
+
generation: z.number(),
|
|
25
|
+
});
|
|
26
|
+
const jsonContent = (schema, description) => ({
|
|
27
|
+
description,
|
|
28
|
+
content: { "application/json": { schema } },
|
|
29
|
+
});
|
|
30
|
+
const requiredJsonBody = (schema) => ({
|
|
31
|
+
required: true,
|
|
32
|
+
content: { "application/json": { schema } },
|
|
33
|
+
});
|
|
34
|
+
export const listAppsRoute = createRoute({
|
|
35
|
+
method: "get",
|
|
36
|
+
path: "/",
|
|
37
|
+
request: { query: appListQuerySchema },
|
|
38
|
+
responses: { 200: jsonContent(listEnvelopeSchema, "Paginated app registrations") },
|
|
39
|
+
});
|
|
40
|
+
export const createCustomAppRoute = createRoute({
|
|
41
|
+
method: "post",
|
|
42
|
+
path: "/",
|
|
43
|
+
request: { body: requiredJsonBody(createCustomAppRegistrationSchema) },
|
|
44
|
+
responses: {
|
|
45
|
+
201: jsonContent(dataEnvelopeSchema, "Created custom app registration"),
|
|
46
|
+
409: jsonContent(errorSchema, "Duplicate app registration"),
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
export const authorizeAppOAuthRoute = createRoute({
|
|
50
|
+
method: "post",
|
|
51
|
+
path: "/oauth/authorize",
|
|
52
|
+
request: { body: requiredJsonBody(appOAuthAuthorizeQuerySchema) },
|
|
53
|
+
responses: {
|
|
54
|
+
200: jsonContent(oauthRedirectResponseSchema, "OAuth authorization redirect target"),
|
|
55
|
+
501: jsonContent(errorSchema, "App OAuth is not configured"),
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
export const issueAppOAuthTokenRoute = createRoute({
|
|
59
|
+
method: "post",
|
|
60
|
+
path: "/oauth/token",
|
|
61
|
+
request: { body: requiredJsonBody(appOAuthTokenSchema) },
|
|
62
|
+
responses: {
|
|
63
|
+
200: jsonContent(tokenResponseSchema, "OAuth token response"),
|
|
64
|
+
501: jsonContent(errorSchema, "App OAuth is not configured"),
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
export const revokeAppInstallationCredentialsRoute = createRoute({
|
|
68
|
+
method: "post",
|
|
69
|
+
path: "/oauth/revoke-installation",
|
|
70
|
+
request: { body: requiredJsonBody(appCredentialRevocationSchema) },
|
|
71
|
+
responses: {
|
|
72
|
+
200: jsonContent(credentialRevocationResponseSchema, "Revoked installation credential generation"),
|
|
73
|
+
501: jsonContent(errorSchema, "App OAuth is not configured"),
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
export const issueAppSessionTokenRoute = createRoute({
|
|
77
|
+
method: "post",
|
|
78
|
+
path: "/installations/{installationId}/session-token",
|
|
79
|
+
request: {
|
|
80
|
+
params: installationIdParamSchema,
|
|
81
|
+
body: requiredJsonBody(appSessionTokenIssueSchema),
|
|
82
|
+
},
|
|
83
|
+
responses: {
|
|
84
|
+
201: jsonContent(dataEnvelopeSchema, "Issued app session token"),
|
|
85
|
+
501: jsonContent(errorSchema, "App session tokens are not configured"),
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
export const exchangeAppSessionTokenRoute = createRoute({
|
|
89
|
+
method: "post",
|
|
90
|
+
path: "/oauth/session-token/exchange",
|
|
91
|
+
request: { body: requiredJsonBody(appSessionTokenExchangeSchema) },
|
|
92
|
+
responses: {
|
|
93
|
+
200: jsonContent(tokenResponseSchema, "Online app token response"),
|
|
94
|
+
501: jsonContent(errorSchema, "App session tokens are not configured"),
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
export const installAppRoute = createRoute({
|
|
98
|
+
method: "post",
|
|
99
|
+
path: "/install",
|
|
100
|
+
request: { body: requiredJsonBody(installAppSchema) },
|
|
101
|
+
responses: { 201: jsonContent(dataEnvelopeSchema, "Installed app") },
|
|
102
|
+
});
|
|
103
|
+
export const listAppInstallationsRoute = createRoute({
|
|
104
|
+
method: "get",
|
|
105
|
+
path: "/installations",
|
|
106
|
+
request: { query: appInstallationListQuerySchema },
|
|
107
|
+
responses: { 200: jsonContent(listEnvelopeSchema, "Paginated app installations") },
|
|
108
|
+
});
|
|
109
|
+
export const getAppInstallationRoute = createRoute({
|
|
110
|
+
method: "get",
|
|
111
|
+
path: "/installations/{installationId}",
|
|
112
|
+
request: { params: installationIdParamSchema },
|
|
113
|
+
responses: {
|
|
114
|
+
200: jsonContent(dataEnvelopeSchema, "App installation detail"),
|
|
115
|
+
404: jsonContent(errorSchema, "App installation not found"),
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
export const listAppInstallationAuditRoute = createRoute({
|
|
119
|
+
method: "get",
|
|
120
|
+
path: "/installations/{installationId}/audit",
|
|
121
|
+
request: { params: installationIdParamSchema, query: appInstallationAuditQuerySchema },
|
|
122
|
+
responses: { 200: jsonContent(dataEnvelopeSchema, "App installation audit events") },
|
|
123
|
+
});
|
|
124
|
+
export const pauseAppInstallationRoute = createRoute({
|
|
125
|
+
method: "post",
|
|
126
|
+
path: "/installations/{installationId}/pause",
|
|
127
|
+
request: { params: installationIdParamSchema, body: requiredJsonBody(lifecycleActionBodySchema) },
|
|
128
|
+
responses: { 200: jsonContent(dataEnvelopeSchema, "Paused app installation") },
|
|
129
|
+
});
|
|
130
|
+
export const resumeAppInstallationRoute = createRoute({
|
|
131
|
+
method: "post",
|
|
132
|
+
path: "/installations/{installationId}/resume",
|
|
133
|
+
request: { params: installationIdParamSchema, body: requiredJsonBody(lifecycleActionBodySchema) },
|
|
134
|
+
responses: { 200: jsonContent(dataEnvelopeSchema, "Resumed app installation") },
|
|
135
|
+
});
|
|
136
|
+
export const uninstallAppInstallationRoute = createRoute({
|
|
137
|
+
method: "post",
|
|
138
|
+
path: "/installations/{installationId}/uninstall",
|
|
139
|
+
request: { params: installationIdParamSchema, body: requiredJsonBody(lifecycleActionBodySchema) },
|
|
140
|
+
responses: { 200: jsonContent(dataEnvelopeSchema, "Uninstalled app installation") },
|
|
141
|
+
});
|
|
142
|
+
export const activateAppInstallationRoute = createRoute({
|
|
143
|
+
method: "post",
|
|
144
|
+
path: "/installations/{installationId}/activate",
|
|
145
|
+
request: {
|
|
146
|
+
params: installationIdParamSchema,
|
|
147
|
+
body: requiredJsonBody(activateInstallationBodySchema),
|
|
148
|
+
},
|
|
149
|
+
responses: { 200: jsonContent(dataEnvelopeSchema, "Activated app installation release") },
|
|
150
|
+
});
|
|
151
|
+
export const previewAppInstallationPurgeRoute = createRoute({
|
|
152
|
+
method: "post",
|
|
153
|
+
path: "/installations/{installationId}/purge-preview",
|
|
154
|
+
request: { params: installationIdParamSchema, body: requiredJsonBody(lifecycleActionBodySchema) },
|
|
155
|
+
responses: { 200: jsonContent(dataEnvelopeSchema, "App installation purge preview") },
|
|
156
|
+
});
|
|
157
|
+
export const getAppRoute = createRoute({
|
|
158
|
+
method: "get",
|
|
159
|
+
path: "/{appId}",
|
|
160
|
+
request: { params: appIdParamSchema },
|
|
161
|
+
responses: {
|
|
162
|
+
200: jsonContent(dataEnvelopeSchema, "App registration"),
|
|
163
|
+
404: jsonContent(errorSchema, "App not found"),
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
export const listAppReleasesRoute = createRoute({
|
|
167
|
+
method: "get",
|
|
168
|
+
path: "/{appId}/releases",
|
|
169
|
+
request: { params: appIdParamSchema },
|
|
170
|
+
responses: { 200: jsonContent(dataEnvelopeSchema, "App releases") },
|
|
171
|
+
});
|
|
172
|
+
export const createAppReleaseRoute = createRoute({
|
|
173
|
+
method: "post",
|
|
174
|
+
path: "/{appId}/releases",
|
|
175
|
+
request: { params: appIdParamSchema, body: requiredJsonBody(releaseManifestUploadSchema) },
|
|
176
|
+
responses: { 201: jsonContent(releaseCreateResponseSchema, "Created app release") },
|
|
177
|
+
});
|
|
178
|
+
export const fetchAppReleaseRoute = createRoute({
|
|
179
|
+
method: "post",
|
|
180
|
+
path: "/{appId}/releases/fetch",
|
|
181
|
+
request: { params: appIdParamSchema, body: requiredJsonBody(releaseManifestFetchSchema) },
|
|
182
|
+
responses: { 201: jsonContent(releaseCreateResponseSchema, "Fetched app release") },
|
|
183
|
+
});
|
|
184
|
+
export const listAppWebhooksRoute = createRoute({
|
|
185
|
+
method: "get",
|
|
186
|
+
path: "/installations/{installationId}/webhooks",
|
|
187
|
+
request: { params: installationIdParamSchema },
|
|
188
|
+
responses: { 200: jsonContent(dataEnvelopeSchema, "App webhook delivery health") },
|
|
189
|
+
});
|
|
190
|
+
export const replayAppWebhookRoute = createRoute({
|
|
191
|
+
method: "post",
|
|
192
|
+
path: "/installations/{installationId}/webhooks/replay",
|
|
193
|
+
request: { params: installationIdParamSchema, body: requiredJsonBody(appWebhookReplaySchema) },
|
|
194
|
+
responses: { 202: jsonContent(dataEnvelopeSchema, "Replayed app webhook delivery") },
|
|
195
|
+
});
|
package/dist/routes.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { OpenAPIHono } from "@hono/zod-openapi";
|
|
1
2
|
import type { EventBus } from "@voyant-travel/core/events";
|
|
2
3
|
import type { createCustomFieldsService } from "@voyant-travel/custom-fields";
|
|
3
4
|
import type { AccessCatalog } from "@voyant-travel/types/api-keys";
|
|
4
5
|
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
|
5
|
-
import { Hono } from "hono";
|
|
6
6
|
import { type AppsServiceOptions } from "./service.js";
|
|
7
7
|
type CustomFieldsService = ReturnType<typeof createCustomFieldsService>;
|
|
8
8
|
type Env = {
|
|
@@ -38,5 +38,5 @@ export interface AppsAdminRouteOptions extends AppsServiceOptions {
|
|
|
38
38
|
eventBus?: EventBus;
|
|
39
39
|
customFields?: CustomFieldsService;
|
|
40
40
|
}
|
|
41
|
-
export declare function createAppsAdminRoutes(options?: AppsAdminRouteOptions):
|
|
41
|
+
export declare function createAppsAdminRoutes(options?: AppsAdminRouteOptions): OpenAPIHono<Env, {}, "/">;
|
|
42
42
|
export {};
|
package/dist/routes.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { OpenAPIHono } from "@hono/zod-openapi";
|
|
2
|
+
import { openApiValidationHook, parseJsonBody, parseQuery, RequestValidationError, requireUserId, } from "@voyant-travel/hono";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { activateInstallationBodySchema, appCredentialRevocationSchema, appInstallationAuditQuerySchema, appInstallationListQuerySchema, appListQuerySchema, appOAuthAuthorizeQuerySchema, appOAuthTokenSchema, appSessionTokenExchangeSchema, appSessionTokenIssueSchema, appWebhookReplaySchema, createCustomAppRegistrationSchema, installAppSchema, lifecycleActionBodySchema, releaseManifestFetchSchema, releaseManifestUploadSchema, } from "./contracts.js";
|
|
5
5
|
import { listAppReleases, listInstallationAudit, listInstallationSummaries, loadInstallationDetail, } from "./installation-read-model.js";
|
|
6
6
|
import { createAppInstallationService } from "./installation-service.js";
|
|
7
7
|
import { createAppOAuthService } from "./oauth-service.js";
|
|
8
|
+
import { activateAppInstallationRoute, authorizeAppOAuthRoute, createAppReleaseRoute, createCustomAppRoute, exchangeAppSessionTokenRoute, fetchAppReleaseRoute, getAppInstallationRoute, getAppRoute, installAppRoute, issueAppOAuthTokenRoute, issueAppSessionTokenRoute, listAppInstallationAuditRoute, listAppInstallationsRoute, listAppReleasesRoute, listAppsRoute, listAppWebhooksRoute, pauseAppInstallationRoute, previewAppInstallationPurgeRoute, replayAppWebhookRoute, resumeAppInstallationRoute, revokeAppInstallationCredentialsRoute, uninstallAppInstallationRoute, } from "./routes-openapi.js";
|
|
8
9
|
import { createAppsService } from "./service.js";
|
|
9
10
|
import { createAppSessionTokenService } from "./session-token-service.js";
|
|
10
11
|
import { listAppWebhookHealth, replayAppWebhookDelivery } from "./webhook-delivery.js";
|
|
11
12
|
const appIdParamSchema = z.object({ appId: z.string().min(1) });
|
|
12
13
|
const installationIdParamSchema = z.object({ installationId: z.string().min(1) });
|
|
13
14
|
export function createAppsAdminRoutes(options = {}) {
|
|
14
|
-
const routes = new
|
|
15
|
+
const routes = new OpenAPIHono({ defaultHook: openApiValidationHook });
|
|
15
16
|
const service = createAppsService(options);
|
|
16
17
|
const installations = createAppInstallationService({
|
|
17
18
|
deploymentId: options.oauth?.deploymentId ?? options.deploymentId,
|
|
@@ -28,11 +29,11 @@ export function createAppsAdminRoutes(options = {}) {
|
|
|
28
29
|
oauth,
|
|
29
30
|
})
|
|
30
31
|
: null;
|
|
31
|
-
routes.
|
|
32
|
+
routes.openapi(listAppsRoute, async (c) => {
|
|
32
33
|
const query = parseQuery(c, appListQuerySchema);
|
|
33
34
|
return c.json(await service.list(c.get("db"), query), 200);
|
|
34
35
|
});
|
|
35
|
-
routes.
|
|
36
|
+
routes.openapi(createCustomAppRoute, async (c) => {
|
|
36
37
|
const body = await parseJsonBody(c, createCustomAppRegistrationSchema);
|
|
37
38
|
const app = await service.createCustomApp(c.get("db"), body);
|
|
38
39
|
return c.json({ data: app }, 201);
|
|
@@ -40,7 +41,7 @@ export function createAppsAdminRoutes(options = {}) {
|
|
|
40
41
|
// Consent approval mutates state (installation, grants, authorization code),
|
|
41
42
|
// so it must never be reachable through read-scoped GET requests. The admin
|
|
42
43
|
// consent UI submits the approval and performs the redirect itself.
|
|
43
|
-
routes.
|
|
44
|
+
routes.openapi(authorizeAppOAuthRoute, async (c) => {
|
|
44
45
|
if (!oauth)
|
|
45
46
|
return c.json({ error: "App OAuth is not configured" }, 501);
|
|
46
47
|
const body = await parseJsonBody(c, appOAuthAuthorizeQuerySchema);
|
|
@@ -60,7 +61,7 @@ export function createAppsAdminRoutes(options = {}) {
|
|
|
60
61
|
redirectUrl.searchParams.set("state", result.state);
|
|
61
62
|
return c.json({ data: { redirectUrl: redirectUrl.toString(), state: result.state } }, 200);
|
|
62
63
|
});
|
|
63
|
-
routes.
|
|
64
|
+
routes.openapi(issueAppOAuthTokenRoute, async (c) => {
|
|
64
65
|
if (!oauth)
|
|
65
66
|
return c.json({ error: "App OAuth is not configured" }, 501);
|
|
66
67
|
const body = await parseJsonBody(c, appOAuthTokenSchema);
|
|
@@ -91,7 +92,7 @@ export function createAppsAdminRoutes(options = {}) {
|
|
|
91
92
|
});
|
|
92
93
|
return c.json(token, 200);
|
|
93
94
|
});
|
|
94
|
-
routes.
|
|
95
|
+
routes.openapi(revokeAppInstallationCredentialsRoute, async (c) => {
|
|
95
96
|
if (!oauth)
|
|
96
97
|
return c.json({ error: "App OAuth is not configured" }, 501);
|
|
97
98
|
const body = await parseJsonBody(c, appCredentialRevocationSchema);
|
|
@@ -101,7 +102,7 @@ export function createAppsAdminRoutes(options = {}) {
|
|
|
101
102
|
// Staff-authenticated: the admin host requests a short-lived session token for
|
|
102
103
|
// the current viewer + entity/slot context. The viewer is taken from the
|
|
103
104
|
// authenticated session, never from the frame.
|
|
104
|
-
routes.
|
|
105
|
+
routes.openapi(issueAppSessionTokenRoute, async (c) => {
|
|
105
106
|
if (!sessionTokens)
|
|
106
107
|
return c.json({ error: "App session tokens are not configured" }, 501);
|
|
107
108
|
const { installationId } = parseInstallationParams(c.req.param());
|
|
@@ -117,7 +118,7 @@ export function createAppsAdminRoutes(options = {}) {
|
|
|
117
118
|
});
|
|
118
119
|
// App-backend-facing: exchange a presented session token for online actor
|
|
119
120
|
// access. Client-authenticated; bounded by viewer ∩ app grants.
|
|
120
|
-
routes.
|
|
121
|
+
routes.openapi(exchangeAppSessionTokenRoute, async (c) => {
|
|
121
122
|
if (!sessionTokens)
|
|
122
123
|
return c.json({ error: "App session tokens are not configured" }, 501);
|
|
123
124
|
const body = await parseJsonBody(c, appSessionTokenExchangeSchema);
|
|
@@ -130,7 +131,7 @@ export function createAppsAdminRoutes(options = {}) {
|
|
|
130
131
|
});
|
|
131
132
|
return c.json(token, 200);
|
|
132
133
|
});
|
|
133
|
-
routes.
|
|
134
|
+
routes.openapi(installAppRoute, async (c) => {
|
|
134
135
|
const body = await parseJsonBody(c, installAppSchema);
|
|
135
136
|
// The deployment id is a runtime value (not known at graph-composition
|
|
136
137
|
// time), so resolve it per request: explicit body → runtime env →
|
|
@@ -147,11 +148,11 @@ export function createAppsAdminRoutes(options = {}) {
|
|
|
147
148
|
});
|
|
148
149
|
return c.json({ data: result }, 201);
|
|
149
150
|
});
|
|
150
|
-
routes.
|
|
151
|
+
routes.openapi(listAppInstallationsRoute, async (c) => {
|
|
151
152
|
const query = parseQuery(c, appInstallationListQuerySchema);
|
|
152
153
|
return c.json(await listInstallationSummaries(c.get("db"), query), 200);
|
|
153
154
|
});
|
|
154
|
-
routes.
|
|
155
|
+
routes.openapi(getAppInstallationRoute, async (c) => {
|
|
155
156
|
const { installationId } = parseInstallationParams(c.req.param());
|
|
156
157
|
const detail = await loadInstallationDetail(c.get("db"), installationId, {
|
|
157
158
|
platformApiVersion: options.platformApiVersion,
|
|
@@ -160,19 +161,19 @@ export function createAppsAdminRoutes(options = {}) {
|
|
|
160
161
|
? c.json({ data: detail }, 200)
|
|
161
162
|
: c.json({ error: "App installation not found" }, 404);
|
|
162
163
|
});
|
|
163
|
-
routes.
|
|
164
|
+
routes.openapi(listAppInstallationAuditRoute, async (c) => {
|
|
164
165
|
const { installationId } = parseInstallationParams(c.req.param());
|
|
165
166
|
const query = parseQuery(c, appInstallationAuditQuerySchema);
|
|
166
167
|
const data = await listInstallationAudit(c.get("db"), installationId, query.limit);
|
|
167
168
|
return c.json({ data }, 200);
|
|
168
169
|
});
|
|
169
|
-
routes.
|
|
170
|
+
routes.openapi(pauseAppInstallationRoute, async (c) => {
|
|
170
171
|
const { installationId } = parseInstallationParams(c.req.param());
|
|
171
172
|
const body = await parseJsonBody(c, lifecycleActionBodySchema);
|
|
172
173
|
const result = await installations.pause(c.get("db"), { installationId, actorId: body.actorId });
|
|
173
174
|
return c.json({ data: result }, 200);
|
|
174
175
|
});
|
|
175
|
-
routes.
|
|
176
|
+
routes.openapi(resumeAppInstallationRoute, async (c) => {
|
|
176
177
|
const { installationId } = parseInstallationParams(c.req.param());
|
|
177
178
|
const body = await parseJsonBody(c, lifecycleActionBodySchema);
|
|
178
179
|
const result = await installations.resume(c.get("db"), {
|
|
@@ -181,7 +182,7 @@ export function createAppsAdminRoutes(options = {}) {
|
|
|
181
182
|
});
|
|
182
183
|
return c.json({ data: result }, 200);
|
|
183
184
|
});
|
|
184
|
-
routes.
|
|
185
|
+
routes.openapi(uninstallAppInstallationRoute, async (c) => {
|
|
185
186
|
const { installationId } = parseInstallationParams(c.req.param());
|
|
186
187
|
const body = await parseJsonBody(c, lifecycleActionBodySchema);
|
|
187
188
|
const result = await installations.uninstall(c.get("db"), {
|
|
@@ -190,7 +191,7 @@ export function createAppsAdminRoutes(options = {}) {
|
|
|
190
191
|
});
|
|
191
192
|
return c.json({ data: result }, 200);
|
|
192
193
|
});
|
|
193
|
-
routes.
|
|
194
|
+
routes.openapi(activateAppInstallationRoute, async (c) => {
|
|
194
195
|
const { installationId } = parseInstallationParams(c.req.param());
|
|
195
196
|
const body = await parseJsonBody(c, activateInstallationBodySchema);
|
|
196
197
|
const result = await installations.upgrade(c.get("db"), {
|
|
@@ -200,7 +201,7 @@ export function createAppsAdminRoutes(options = {}) {
|
|
|
200
201
|
});
|
|
201
202
|
return c.json({ data: result }, 200);
|
|
202
203
|
});
|
|
203
|
-
routes.
|
|
204
|
+
routes.openapi(previewAppInstallationPurgeRoute, async (c) => {
|
|
204
205
|
const { installationId } = parseInstallationParams(c.req.param());
|
|
205
206
|
const body = await parseJsonBody(c, lifecycleActionBodySchema);
|
|
206
207
|
const result = await installations.purgePreview(c.get("db"), {
|
|
@@ -209,33 +210,33 @@ export function createAppsAdminRoutes(options = {}) {
|
|
|
209
210
|
});
|
|
210
211
|
return c.json({ data: result }, 200);
|
|
211
212
|
});
|
|
212
|
-
routes.
|
|
213
|
+
routes.openapi(getAppRoute, async (c) => {
|
|
213
214
|
const { appId } = parseParams(c.req.param());
|
|
214
215
|
const app = await service.get(c.get("db"), appId);
|
|
215
216
|
return app ? c.json({ data: app }, 200) : c.json({ error: "App not found" }, 404);
|
|
216
217
|
});
|
|
217
|
-
routes.
|
|
218
|
+
routes.openapi(listAppReleasesRoute, async (c) => {
|
|
218
219
|
const { appId } = parseParams(c.req.param());
|
|
219
220
|
const data = await listAppReleases(c.get("db"), appId);
|
|
220
221
|
return c.json({ data }, 200);
|
|
221
222
|
});
|
|
222
|
-
routes.
|
|
223
|
+
routes.openapi(createAppReleaseRoute, async (c) => {
|
|
223
224
|
const { appId } = parseParams(c.req.param());
|
|
224
225
|
const body = await parseJsonBody(c, releaseManifestUploadSchema);
|
|
225
226
|
const result = await service.releaseFromUpload(c.get("db"), appId, body);
|
|
226
227
|
return c.json({ data: result.release, digest: result.digest, created: result.created }, 201);
|
|
227
228
|
});
|
|
228
|
-
routes.
|
|
229
|
+
routes.openapi(fetchAppReleaseRoute, async (c) => {
|
|
229
230
|
const { appId } = parseParams(c.req.param());
|
|
230
231
|
const body = await parseJsonBody(c, releaseManifestFetchSchema);
|
|
231
232
|
const result = await service.releaseFromFetch(c.get("db"), appId, body);
|
|
232
233
|
return c.json({ data: result.release, digest: result.digest, created: result.created }, 201);
|
|
233
234
|
});
|
|
234
|
-
routes.
|
|
235
|
+
routes.openapi(listAppWebhooksRoute, async (c) => {
|
|
235
236
|
const { installationId } = parseInstallationParams(c.req.param());
|
|
236
237
|
return c.json(await listAppWebhookHealth(c.get("db"), installationId), 200);
|
|
237
238
|
});
|
|
238
|
-
routes.
|
|
239
|
+
routes.openapi(replayAppWebhookRoute, async (c) => {
|
|
239
240
|
parseInstallationParams(c.req.param());
|
|
240
241
|
const body = await parseJsonBody(c, appWebhookReplaySchema);
|
|
241
242
|
const delivery = await replayAppWebhookDelivery(c.get("db"), {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function createAppsRuntimePortContribution(): Readonly<Record<string, unknown>>;
|
package/dist/voyant.js
CHANGED
|
@@ -33,6 +33,7 @@ export const appsVoyantModule = defineModule({
|
|
|
33
33
|
id: "@voyant-travel/apps#api.admin",
|
|
34
34
|
surface: "admin",
|
|
35
35
|
mount: "apps",
|
|
36
|
+
openapi: { document: "apps" },
|
|
36
37
|
resource: "apps",
|
|
37
38
|
transactional: true,
|
|
38
39
|
runtime: {
|
|
@@ -111,7 +112,18 @@ export const appsVoyantModule = defineModule({
|
|
|
111
112
|
label: "Own custom-field definitions",
|
|
112
113
|
description: "Read and write custom-field definitions owned by the app.",
|
|
113
114
|
remoteSafe: true,
|
|
114
|
-
actions: [
|
|
115
|
+
actions: [
|
|
116
|
+
{
|
|
117
|
+
action: "read",
|
|
118
|
+
label: "Read custom-field definitions",
|
|
119
|
+
description: "Read definitions owned by the app.",
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
action: "write",
|
|
123
|
+
label: "Write custom-field definitions",
|
|
124
|
+
description: "Create and update definitions owned by the app.",
|
|
125
|
+
},
|
|
126
|
+
],
|
|
115
127
|
wildcard: "explicit-resource",
|
|
116
128
|
},
|
|
117
129
|
{
|
|
@@ -120,7 +132,38 @@ export const appsVoyantModule = defineModule({
|
|
|
120
132
|
label: "Own custom-field values",
|
|
121
133
|
description: "Read and write app-owned custom-field values by target.",
|
|
122
134
|
remoteSafe: true,
|
|
123
|
-
actions: [
|
|
135
|
+
actions: [
|
|
136
|
+
{
|
|
137
|
+
action: "read",
|
|
138
|
+
label: "Read custom-field values",
|
|
139
|
+
description: "Read custom-field values owned by the app.",
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
action: "write",
|
|
143
|
+
label: "Write custom-field values",
|
|
144
|
+
description: "Write custom-field values owned by the app.",
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
action: "booking",
|
|
148
|
+
label: "Booking custom fields",
|
|
149
|
+
description: "Access app-owned custom-field values on bookings.",
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
action: "person",
|
|
153
|
+
label: "Person custom fields",
|
|
154
|
+
description: "Access app-owned custom-field values on people.",
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
action: "organization",
|
|
158
|
+
label: "Organization custom fields",
|
|
159
|
+
description: "Access app-owned custom-field values on organizations.",
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
action: "invoice",
|
|
163
|
+
label: "Invoice custom fields",
|
|
164
|
+
description: "Access app-owned custom-field values on invoices.",
|
|
165
|
+
},
|
|
166
|
+
],
|
|
124
167
|
wildcard: "explicit-resource",
|
|
125
168
|
},
|
|
126
169
|
{
|
|
@@ -133,7 +176,11 @@ export const appsVoyantModule = defineModule({
|
|
|
133
176
|
description: "Read webhook health and request replay.",
|
|
134
177
|
remoteSafe: true,
|
|
135
178
|
actions: [
|
|
136
|
-
|
|
179
|
+
{
|
|
180
|
+
action: "read",
|
|
181
|
+
label: "Read app webhooks",
|
|
182
|
+
description: "Read app webhook subscription and delivery health.",
|
|
183
|
+
},
|
|
137
184
|
{ action: "replay", label: "Replay webhooks", description: "Replay deliveries." },
|
|
138
185
|
],
|
|
139
186
|
},
|
|
@@ -143,7 +190,13 @@ export const appsVoyantModule = defineModule({
|
|
|
143
190
|
label: "App audit history",
|
|
144
191
|
description: "Read audit history owned by the app.",
|
|
145
192
|
remoteSafe: true,
|
|
146
|
-
actions: [
|
|
193
|
+
actions: [
|
|
194
|
+
{
|
|
195
|
+
action: "read",
|
|
196
|
+
label: "Read app audit history",
|
|
197
|
+
description: "Read audit events owned by the app.",
|
|
198
|
+
},
|
|
199
|
+
],
|
|
147
200
|
},
|
|
148
201
|
{
|
|
149
202
|
id: "@voyant-travel/apps#access.online-token",
|
|
@@ -151,7 +204,14 @@ export const appsVoyantModule = defineModule({
|
|
|
151
204
|
label: "Online token exchange",
|
|
152
205
|
description: "Exchange admin session context for online app access.",
|
|
153
206
|
remoteSafe: true,
|
|
154
|
-
actions: [
|
|
207
|
+
actions: [
|
|
208
|
+
{
|
|
209
|
+
action: "exchange",
|
|
210
|
+
label: "Exchange online token",
|
|
211
|
+
description: "Exchange admin session context for online app access.",
|
|
212
|
+
wildcard: "explicit",
|
|
213
|
+
},
|
|
214
|
+
],
|
|
155
215
|
},
|
|
156
216
|
{
|
|
157
217
|
id: "@voyant-travel/apps#access.finance-documents",
|
|
@@ -159,7 +219,13 @@ export const appsVoyantModule = defineModule({
|
|
|
159
219
|
label: "Finance documents",
|
|
160
220
|
description: "Read finance documents visible to remote accounting apps.",
|
|
161
221
|
remoteSafe: true,
|
|
162
|
-
actions: [
|
|
222
|
+
actions: [
|
|
223
|
+
{
|
|
224
|
+
action: "read",
|
|
225
|
+
label: "Read finance documents",
|
|
226
|
+
description: "Read finance documents visible to the app.",
|
|
227
|
+
},
|
|
228
|
+
],
|
|
163
229
|
wildcard: "explicit-resource",
|
|
164
230
|
},
|
|
165
231
|
{
|
|
@@ -169,9 +235,27 @@ export const appsVoyantModule = defineModule({
|
|
|
169
235
|
description: "Request approved finance document actions.",
|
|
170
236
|
remoteSafe: true,
|
|
171
237
|
actions: [
|
|
172
|
-
{
|
|
173
|
-
|
|
174
|
-
|
|
238
|
+
{
|
|
239
|
+
action: "issue",
|
|
240
|
+
label: "Issue finance documents",
|
|
241
|
+
description: "Request approved finance document issuance.",
|
|
242
|
+
sensitive: true,
|
|
243
|
+
wildcard: "explicit",
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
action: "retry",
|
|
247
|
+
label: "Retry finance actions",
|
|
248
|
+
description: "Request approved finance action retry.",
|
|
249
|
+
sensitive: true,
|
|
250
|
+
wildcard: "explicit",
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
action: "reconcile",
|
|
254
|
+
label: "Reconcile finance documents",
|
|
255
|
+
description: "Request approved finance document reconciliation.",
|
|
256
|
+
sensitive: true,
|
|
257
|
+
wildcard: "explicit",
|
|
258
|
+
},
|
|
175
259
|
],
|
|
176
260
|
wildcard: "explicit-resource",
|
|
177
261
|
},
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"openapi": "3.1.0",
|
|
3
|
+
"info": {
|
|
4
|
+
"title": "Voyant Apps API",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"description": "Operator app registration, release, installation, consent, token, and webhook governance routes."
|
|
7
|
+
},
|
|
8
|
+
"paths": {
|
|
9
|
+
"/v1/admin/apps": {
|
|
10
|
+
"get": { "summary": "List app registrations" },
|
|
11
|
+
"post": { "summary": "Create a custom app registration" }
|
|
12
|
+
},
|
|
13
|
+
"/v1/admin/apps/oauth/authorize": {
|
|
14
|
+
"post": { "summary": "Approve app OAuth consent" }
|
|
15
|
+
},
|
|
16
|
+
"/v1/admin/apps/oauth/token": {
|
|
17
|
+
"post": { "summary": "Issue an app OAuth token" }
|
|
18
|
+
},
|
|
19
|
+
"/v1/admin/apps/oauth/revoke-installation": {
|
|
20
|
+
"post": { "summary": "Revoke installation credentials" }
|
|
21
|
+
},
|
|
22
|
+
"/v1/admin/apps/oauth/session-token/exchange": {
|
|
23
|
+
"post": { "summary": "Exchange an app session token" }
|
|
24
|
+
},
|
|
25
|
+
"/v1/admin/apps/install": {
|
|
26
|
+
"post": { "summary": "Install an app release" }
|
|
27
|
+
},
|
|
28
|
+
"/v1/admin/apps/installations": {
|
|
29
|
+
"get": { "summary": "List app installations" }
|
|
30
|
+
},
|
|
31
|
+
"/v1/admin/apps/installations/{installationId}": {
|
|
32
|
+
"get": { "summary": "Get app installation detail" }
|
|
33
|
+
},
|
|
34
|
+
"/v1/admin/apps/installations/{installationId}/audit": {
|
|
35
|
+
"get": { "summary": "List app installation audit events" }
|
|
36
|
+
},
|
|
37
|
+
"/v1/admin/apps/installations/{installationId}/pause": {
|
|
38
|
+
"post": { "summary": "Pause an app installation" }
|
|
39
|
+
},
|
|
40
|
+
"/v1/admin/apps/installations/{installationId}/resume": {
|
|
41
|
+
"post": { "summary": "Resume an app installation" }
|
|
42
|
+
},
|
|
43
|
+
"/v1/admin/apps/installations/{installationId}/uninstall": {
|
|
44
|
+
"post": { "summary": "Uninstall an app installation" }
|
|
45
|
+
},
|
|
46
|
+
"/v1/admin/apps/installations/{installationId}/activate": {
|
|
47
|
+
"post": { "summary": "Activate an app installation release" }
|
|
48
|
+
},
|
|
49
|
+
"/v1/admin/apps/installations/{installationId}/purge-preview": {
|
|
50
|
+
"post": { "summary": "Preview app installation purge impact" }
|
|
51
|
+
},
|
|
52
|
+
"/v1/admin/apps/installations/{installationId}/session-token": {
|
|
53
|
+
"post": { "summary": "Issue an app extension session token" }
|
|
54
|
+
},
|
|
55
|
+
"/v1/admin/apps/installations/{installationId}/webhooks": {
|
|
56
|
+
"get": { "summary": "List app webhook delivery health" }
|
|
57
|
+
},
|
|
58
|
+
"/v1/admin/apps/installations/{installationId}/webhooks/replay": {
|
|
59
|
+
"post": { "summary": "Replay an app webhook delivery" }
|
|
60
|
+
},
|
|
61
|
+
"/v1/admin/apps/{appId}": {
|
|
62
|
+
"get": { "summary": "Get an app registration" }
|
|
63
|
+
},
|
|
64
|
+
"/v1/admin/apps/{appId}/releases": {
|
|
65
|
+
"get": { "summary": "List app releases" },
|
|
66
|
+
"post": { "summary": "Create an app release from an uploaded manifest" }
|
|
67
|
+
},
|
|
68
|
+
"/v1/admin/apps/{appId}/releases/fetch": {
|
|
69
|
+
"post": { "summary": "Create an app release from a fetched manifest" }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|