UNPKG

@apistudio/apim-cli

Version:

CLI for API Management Products

168 lines (167 loc) 5.15 kB
/** * Copyright IBM Corp. 2024, 2025 */ import { z } from 'zod'; import { BaseModel, IfConditionSchema, RequestSkippedSchema, StopOnFailSchema, } from './shared.schema.js'; import { EnvironmentSchema } from './environment.schema.js'; import { AssertionSchema } from './assertions.schema.js'; const refRule = z .string() .refine((val) => val !== '', { message: '$ref cannot be an empty string', }) .optional(); const apiRefRule = z .union([ z.string().refine((val) => val !== '', { message: '$ref cannot be an empty string', }), z.array(z.string().refine((val) => val !== '', { message: '$ref array items cannot be empty strings', })), ]) .optional(); const RawPayloadSchema = z .object({ json: z.string().optional(), js: z.string().optional(), html: z.string().optional(), xml: z.string().optional(), }) .strict() .optional(); const KeyValueSchema = z .array(z.object({ key: z.string(), value: z.any().refine((val) => val !== undefined, { message: 'value is required', }), type: z.string().optional(), })) .optional(); export const PayloadUnionSchema = z .object({ raw: RawPayloadSchema, urlEncodedFormData: KeyValueSchema, formData: KeyValueSchema, }) .refine((data) => { const keys = ['raw', 'urlEncodedFormData', 'formData']; const presentKeys = keys.filter((key) => data[key] !== undefined); return presentKeys.length === 1; }, { message: 'Exactly one of raw, urlEncodedFormData, or formData must be provided in payload', }); const ApiRefOrEndpointSchema = z .object({ $ref: apiRefRule, $endpoint: z.string().optional(), }) .refine((data) => (data.$ref && !data.$endpoint) || (!data.$ref && data.$endpoint), { message: 'Either $ref or $endpoint must be provided, but not both in api', path: ['$ref', '$endpoint'], }); const AssertionRefSchema = z .object({ $ref: refRule, assertions: z.array(AssertionSchema).optional(), }) .refine((data) => data == undefined || data.$ref !== undefined || (data.assertions !== undefined && data.assertions.length > 0), { message: 'Either $ref or assertions (non-empty array) with complete data must be provided', path: ['$ref', 'assertions'], }); const EnvironmentSpecSchema = z .object({ $ref: refRule, variables: z.array(EnvironmentSchema).optional(), }) .refine((data) => data == undefined || data.$ref !== undefined || (data.variables !== undefined && data.variables.length > 0), { message: 'Either $ref or variables (non-empty array) must be provided in environment', path: ['$ref', 'variables'], }); const BasicAuthSchema = z.object({ username: z.string(), password: z.string(), }); export const AuthSchema = z .object({ noauth: z.boolean().optional(), bearerToken: z.string().optional(), basicAuth: BasicAuthSchema.optional(), }) .refine((data) => { const keys = ['noauth', 'bearerToken', 'basicAuth']; const present = keys.filter((key) => data[key] !== undefined); return present.length <= 1; }, { message: 'Only one of noauth, bearerToken, or basicAuth must be provided in auth', path: ['auth'], }); export const TestStepSchema = z.object({ endpoint: z.string().optional(), method: z.string(), if: IfConditionSchema.optional(), stopOnFail: StopOnFailSchema.optional(), skipped: RequestSkippedSchema.optional(), resource: z.string(), headers: z .array(z.object({ key: z.string(), value: z.any().refine((val) => val !== undefined, { message: 'value is required in headers', }), description: z.string().optional(), })) .optional(), auth: AuthSchema.optional(), payload: PayloadUnionSchema.optional(), settings: z .object({ sslVerification: z.boolean().optional(), encodeURL: z.boolean().optional(), }) .optional(), parameters: KeyValueSchema.optional(), assertions: z .union([ // New format: array of objects with $ref z.array(AssertionRefSchema.optional()), // Single assertion with direct $ref property AssertionRefSchema, ]) .optional(), var: z .union([ z.string(), z.array(z.union([ z.record(z.string(), z.string()), z.object({ key: z.string(), value: z.string(), }), ])), ]) .optional(), }); export const TestSchema = BaseModel.extend({ kind: z.literal('test'), spec: z.object({ // will be the url to make the request api: ApiRefOrEndpointSchema, environment: z .union([ // New format: array of objects with $ref z.array(EnvironmentSpecSchema.optional()), // Single assertion with direct $ref property EnvironmentSpecSchema, ]) .optional(), // tests which will have path and assertions request: z.array(TestStepSchema), }), vcmId: z.string().optional(), });