diff --git a/src/server/components/public-api/utils/init-public-api-swagger.ts b/src/server/components/public-api/utils/init-public-api-swagger.ts index acc8160618..3a8248c61b 100644 --- a/src/server/components/public-api/utils/init-public-api-swagger.ts +++ b/src/server/components/public-api/utils/init-public-api-swagger.ts @@ -93,6 +93,8 @@ export const initPublicApiSwagger = (app: ExpressKit) => { const swaggerOptions = { url: jsonPath, validatorUrl: null, + tagsSorter: 'alpha', + operationsSorter: 'alpha', }; app.express.use( diff --git a/src/shared/components/public-api/utils/index.ts b/src/shared/components/public-api/utils/index.ts new file mode 100644 index 0000000000..b4b944fbce --- /dev/null +++ b/src/shared/components/public-api/utils/index.ts @@ -0,0 +1,11 @@ +const registeredComponents = new Set(); + +export const registerComponentId = (id: string): string => { + if (registeredComponents.has(id)) { + throw new Error(`OpenAPI component ${id} is already registered`); + } + + registeredComponents.add(id); + + return id; +}; diff --git a/src/shared/schema/gateway-utils.ts b/src/shared/schema/gateway-utils.ts index 8762ff96ca..d6f8223bd0 100644 --- a/src/shared/schema/gateway-utils.ts +++ b/src/shared/schema/gateway-utils.ts @@ -23,7 +23,7 @@ const VALIDATION_SCHEMA_KEY = Symbol('$schema'); const registerValidationSchema = (value: T, schema: TypedActionSchema): T => { Object.defineProperty(value, VALIDATION_SCHEMA_KEY, { value: schema, - enumerable: false, + configurable: true, }); return value; @@ -62,6 +62,39 @@ export const createTypedAction = ( return registerValidationSchema(actionConfig, schemaValidationObject); }; +export const createExtendedTypedAction = + ( + actionConfig: ApiServiceActionConfig< + AppContext, + Request, + Response, + TConfigOutput, + TConfigParams, + TConfigTransformed + >, + ) => + (schema: { + resultSchema: z.ZodType; + paramsSchema: z.ZodType; + }) => { + const schemaValidationObject = { + paramsSchema: schema.paramsSchema, + resultSchema: schema.resultSchema, + }; + + return registerValidationSchema( + actionConfig as unknown as ApiServiceActionConfig< + AppContext, + Request, + Response, + TConfigOutput, + TParams, + TResult + >, + schemaValidationObject, + ); + }; + type AuthArgsData = { userAccessToken?: string; serviceUserAccessToken?: string; diff --git a/src/shared/schema/mix/schemas/ql.ts b/src/shared/schema/mix/schemas/ql.ts index 2b548def92..3a05673d54 100644 --- a/src/shared/schema/mix/schemas/ql.ts +++ b/src/shared/schema/mix/schemas/ql.ts @@ -8,7 +8,7 @@ export const deleteQLChartResultSchema = z.object({}); export const getQLChartArgsSchema = z.strictObject({ chartId: z.string(), - workbookId: z.union([z.string(), z.null()]).optional(), + workbookId: z.string().nullable().optional(), revId: z.string().optional(), includePermissions: z.boolean().optional(), includeLinks: z.boolean().optional(), diff --git a/src/shared/schema/mix/schemas/wizard.ts b/src/shared/schema/mix/schemas/wizard.ts index 8f6bbc0458..96d45cb66a 100644 --- a/src/shared/schema/mix/schemas/wizard.ts +++ b/src/shared/schema/mix/schemas/wizard.ts @@ -8,7 +8,7 @@ export const deleteWizardChartResultSchema = z.object({}); export const getWizardChartArgsSchema = z.strictObject({ chartId: z.string(), - workbookId: z.union([z.string(), z.null()]).optional(), + workbookId: z.string().nullable().optional(), revId: z.string().optional(), includePermissions: z.boolean().optional(), includeLinks: z.boolean().optional(), diff --git a/src/shared/schema/us/actions/editor.ts b/src/shared/schema/us/actions/editor.ts index 60a3bde311..86086e6741 100644 --- a/src/shared/schema/us/actions/editor.ts +++ b/src/shared/schema/us/actions/editor.ts @@ -16,6 +16,7 @@ export const editorActions = { path: () => `${PATH_PREFIX}/entries`, params: ( { + version, type, key, data, @@ -31,6 +32,7 @@ export const editorActions = { ) => { return { body: { + version, scope: 'widget', type, key, @@ -52,9 +54,13 @@ export const editorActions = { method: 'POST', path: ({entryId}) => `${PATH_PREFIX}/entries/${filterUrlFragment(entryId)}`, - params: ({data, mode, revId, meta = {}, links, annotation, description = ''}, headers) => { + params: ( + {version, data, mode, revId, meta = {}, links, annotation, description = ''}, + headers, + ) => { return { body: { + version, mode, meta, data, diff --git a/src/shared/schema/us/schemas/entries/list-directory.ts b/src/shared/schema/us/schemas/entries/list-directory.ts index aa4da12c68..7b9f656e9d 100644 --- a/src/shared/schema/us/schemas/entries/list-directory.ts +++ b/src/shared/schema/us/schemas/entries/list-directory.ts @@ -19,8 +19,6 @@ export const listDirectoryArgsSchema = z.object({ page: z.number().optional(), pageSize: z.number().optional(), includePermissionsInfo: z.boolean().optional(), - // Broken in US controller - // ignoreWorkbookEntries: z.boolean().optional(), }); export const listDirectoryBreadCrumbSchema = z.object({ diff --git a/src/shared/schema/us/types/editor.ts b/src/shared/schema/us/types/editor.ts index bbef19e1ce..192af4b2eb 100644 --- a/src/shared/schema/us/types/editor.ts +++ b/src/shared/schema/us/types/editor.ts @@ -7,6 +7,7 @@ export interface CreateEditorChartResponse extends EntryFields { } export interface CreateEditorChartArgs { + version?: number; type: string; data: EntryFieldData; key?: string; @@ -24,6 +25,7 @@ export interface UpdateEditorChartResponse extends EntryFields { } export interface UpdateEditorChartArgs { + version?: number; entryId: string; mode: 'save' | 'publish'; data: EntryFieldData; diff --git a/src/shared/schema/us/types/fields.ts b/src/shared/schema/us/types/fields.ts index 64383acd43..ababab932d 100644 --- a/src/shared/schema/us/types/fields.ts +++ b/src/shared/schema/us/types/fields.ts @@ -8,6 +8,7 @@ export type EntryFieldPublishedId = null | string; // corresponds to RETURN_COLUMNS from US export interface EntryFields { + version?: number | null; displayAlias?: string | null; createdAt: string; createdBy: string; diff --git a/src/shared/utils/array/assert-non-empty-array.ts b/src/shared/utils/array/assert-non-empty-array.ts new file mode 100644 index 0000000000..f74d567762 --- /dev/null +++ b/src/shared/utils/array/assert-non-empty-array.ts @@ -0,0 +1,5 @@ +export function assertNonEmptyArray(arr: T[], message?: string): asserts arr is [T, ...T[]] { + if (arr.length === 0) { + throw new Error(message ?? 'Array must not be empty!'); + } +} diff --git a/src/shared/utils/array/index.ts b/src/shared/utils/array/index.ts new file mode 100644 index 0000000000..7c3135d54b --- /dev/null +++ b/src/shared/utils/array/index.ts @@ -0,0 +1 @@ +export {assertNonEmptyArray} from './assert-non-empty-array';