diff --git a/libs/backend-api7/src/transformer.ts b/libs/backend-api7/src/transformer.ts index 180ea927..27250395 100644 --- a/libs/backend-api7/src/transformer.ts +++ b/libs/backend-api7/src/transformer.ts @@ -78,7 +78,29 @@ export class ToADC { type: upstream.type, hash_on: upstream.hash_on, key: upstream.key, - checks: upstream.checks, + // API7 Gateway Admin API's "req_headers" maps to ADC's + // "http_req_headers"; every other active health check field is named + // the same. + checks: upstream.checks + ? { + active: { + type: upstream.checks.active.type, + timeout: upstream.checks.active.timeout, + concurrency: upstream.checks.active.concurrency, + host: upstream.checks.active.host, + port: upstream.checks.active.port, + http_method: upstream.checks.active.http_method, + http_path: upstream.checks.active.http_path, + http_req_headers: upstream.checks.active.req_headers, + http_req_body: upstream.checks.active.http_req_body, + https_verify_certificate: + upstream.checks.active.https_verify_certificate, + healthy: upstream.checks.active.healthy, + unhealthy: upstream.checks.active.unhealthy, + }, + passive: upstream.checks.passive, + } + : undefined, nodes: upstream.nodes, scheme: upstream.scheme, retries: upstream.retries, @@ -211,7 +233,9 @@ export class FromADC { name: service.name, desc: service.description, labels: FromADC.transformLabels(service.labels), - upstream: service.upstream as typing.Upstream, + upstream: service.upstream + ? this.transformUpstream(service.upstream) + : undefined, plugins: service.plugins, path_prefix: service.path_prefix, strip_path_prefix: service.strip_path_prefix, @@ -233,7 +257,29 @@ export class FromADC { type: upstream.type, hash_on: upstream.hash_on, key: upstream.key, - checks: upstream.checks, + // ADC's "http_req_headers" maps to API7 Gateway Admin API's + // "req_headers"; every other active health check field is named the + // same. + checks: upstream.checks + ? { + active: { + type: upstream.checks.active.type, + timeout: upstream.checks.active.timeout, + concurrency: upstream.checks.active.concurrency, + host: upstream.checks.active.host, + port: upstream.checks.active.port, + http_method: upstream.checks.active.http_method, + http_path: upstream.checks.active.http_path, + req_headers: upstream.checks.active.http_req_headers, + http_req_body: upstream.checks.active.http_req_body, + https_verify_certificate: + upstream.checks.active.https_verify_certificate, + healthy: upstream.checks.active.healthy, + unhealthy: upstream.checks.active.unhealthy, + }, + passive: upstream.checks.passive, + } + : undefined, nodes: upstream.nodes, scheme: upstream.scheme, retries: upstream.retries, diff --git a/libs/backend-api7/src/typing.ts b/libs/backend-api7/src/typing.ts index eba6deea..d10026fc 100644 --- a/libs/backend-api7/src/typing.ts +++ b/libs/backend-api7/src/typing.ts @@ -4,13 +4,26 @@ import { Labels, Plugins, UpstreamBalancer, - UpstreamHealthCheck, + UpstreamHealthCheck as ADCUpstreamHealthCheck, UpstreamNode, UpstreamPassHost, UpstreamScheme, UpstreamTimeout, } from '@api7/adc-sdk'; +// API7 Gateway's Admin API is APISIX Admin API compatible, so it uses +// "req_headers" on the wire, while ADC exposes it as "http_req_headers"; +// every other active health check field is named the same. +export type UpstreamHealthCheckActive = Omit< + ADCUpstreamHealthCheck['active'], + 'http_req_headers' +> & { + req_headers?: Array; +}; +export type UpstreamHealthCheck = Omit & { + active: UpstreamHealthCheckActive; +}; + export interface Route { id?: string; name: string; diff --git a/libs/backend-api7/test/transformer.spec.ts b/libs/backend-api7/test/transformer.spec.ts index 7b2a1684..996fe2b0 100644 --- a/libs/backend-api7/test/transformer.spec.ts +++ b/libs/backend-api7/test/transformer.spec.ts @@ -40,4 +40,74 @@ describe('Transformer', () => { expect(out.plugins).toEqual(plugins); }); }); + + describe('active health check req_headers round-trip', () => { + it('ToADC.transformUpstream maps req_headers to ADC http_req_headers', () => { + const out = new ToADC().transformUpstream({ + name: 'ups1', + checks: { + active: { + type: 'http', + req_headers: ['X-Foo: bar'], + http_req_body: 'ping', + }, + }, + } as typing.Upstream); + expect(out.checks).toEqual({ + active: { + type: 'http', + http_req_headers: ['X-Foo: bar'], + http_req_body: 'ping', + }, + }); + }); + + it('FromADC.transformUpstream maps ADC http_req_headers back to req_headers', () => { + const out = new FromADC().transformUpstream({ + name: 'ups1', + checks: { + active: { + type: 'http', + http_req_headers: ['X-Foo: bar'], + http_req_body: 'ping', + }, + }, + } as ADCSDK.Upstream); + expect(out.checks).toEqual({ + active: { + type: 'http', + req_headers: ['X-Foo: bar'], + http_req_body: 'ping', + }, + }); + }); + + // Regression: FromADC.transformService used to cast service.upstream + // directly to typing.Upstream instead of routing it through + // transformUpstream, so an inline upstream's http_req_headers never got + // mapped to API7's req_headers. + it('FromADC.transformService maps inline upstream http_req_headers to req_headers', () => { + const out = new FromADC().transformService({ + id: 'svc1', + name: 'svc1', + upstream: { + name: 'ups1', + checks: { + active: { + type: 'http', + http_req_headers: ['X-Foo: bar'], + http_req_body: 'ping', + }, + }, + }, + } as ADCSDK.Service); + expect(out.upstream?.checks).toEqual({ + active: { + type: 'http', + req_headers: ['X-Foo: bar'], + http_req_body: 'ping', + }, + }); + }); + }); }); diff --git a/libs/backend-apisix-standalone/src/operator.ts b/libs/backend-apisix-standalone/src/operator.ts index 0466707f..0f3bf5d3 100644 --- a/libs/backend-apisix-standalone/src/operator.ts +++ b/libs/backend-apisix-standalone/src/operator.ts @@ -480,7 +480,28 @@ export class Operator extends ADCSDK.backend.BackendEventSource { keepalive_pool: res.keepalive_pool, pass_host: res.pass_host, upstream_host: res.upstream_host, - checks: res.checks, + // ADC's "http_req_headers" maps to APISIX config file's "req_headers"; + // every other active health check field is named the same. + checks: res.checks + ? { + active: { + type: res.checks.active.type, + timeout: res.checks.active.timeout, + concurrency: res.checks.active.concurrency, + host: res.checks.active.host, + port: res.checks.active.port, + http_method: res.checks.active.http_method, + http_path: res.checks.active.http_path, + req_headers: res.checks.active.http_req_headers, + http_req_body: res.checks.active.http_req_body, + https_verify_certificate: + res.checks.active.https_verify_certificate, + healthy: res.checks.active.healthy, + unhealthy: res.checks.active.unhealthy, + }, + passive: res.checks.passive, + } + : undefined, discovery_type: res.discovery_type, service_name: res.service_name, discovery_args: res.discovery_args, diff --git a/libs/backend-apisix-standalone/src/transformer.ts b/libs/backend-apisix-standalone/src/transformer.ts index 74495496..b7d975ce 100644 --- a/libs/backend-apisix-standalone/src/transformer.ts +++ b/libs/backend-apisix-standalone/src/transformer.ts @@ -28,7 +28,28 @@ export const toADC = (input: typing.APISIXStandalone) => { pass_host: upstream.pass_host, upstream_host: upstream.upstream_host, - checks: upstream.checks, + // APISIX config file's "req_headers" maps to ADC's "http_req_headers"; + // every other active health check field is named the same. + checks: upstream.checks + ? { + active: { + type: upstream.checks.active.type, + timeout: upstream.checks.active.timeout, + concurrency: upstream.checks.active.concurrency, + host: upstream.checks.active.host, + port: upstream.checks.active.port, + http_method: upstream.checks.active.http_method, + http_path: upstream.checks.active.http_path, + http_req_headers: upstream.checks.active.req_headers, + http_req_body: upstream.checks.active.http_req_body, + https_verify_certificate: + upstream.checks.active.https_verify_certificate, + healthy: upstream.checks.active.healthy, + unhealthy: upstream.checks.active.unhealthy, + }, + passive: upstream.checks.passive, + } + : undefined, discovery_type: upstream.discovery_type, service_name: upstream.service_name, discovery_args: upstream.discovery_args, diff --git a/libs/backend-apisix-standalone/src/typing.ts b/libs/backend-apisix-standalone/src/typing.ts index 25f6ef14..f5621d11 100644 --- a/libs/backend-apisix-standalone/src/typing.ts +++ b/libs/backend-apisix-standalone/src/typing.ts @@ -77,6 +77,18 @@ const upstreamHealthCheckPassiveUnhealthy = z.strictObject({ const upstreamHealthCheckType = z .union([z.literal('http'), z.literal('https'), z.literal('tcp')]) .default('http'); +const methodSchema = z.enum([ + 'GET', + 'POST', + 'PUT', + 'DELETE', + 'PATCH', + 'HEAD', + 'OPTIONS', + 'CONNECT', + 'TRACE', + 'PURGE', +]); const UpstreamSchema = z.strictObject({ ...ModifiedIndex, ...Metadata, @@ -145,9 +157,13 @@ const UpstreamSchema = z.strictObject({ concurrency: z.coerce.number().default(10).optional(), host: z.string().min(1).optional(), port: z.coerce.number().int().min(1).max(65535).optional(), + http_method: methodSchema.default('GET').optional(), http_path: z.string().default('/').optional(), + // APISIX config file field, mapped from/to ADC's "http_req_headers" + // in transformer.ts. + req_headers: z.array(z.string()).min(1).optional(), + http_req_body: z.string().default('').optional(), https_verify_certificate: z.boolean().default(true).optional(), - http_request_headers: z.array(z.string()).min(1).optional(), healthy: z .strictObject({ ...upstreamHealthCheckPassiveHealthy.shape, diff --git a/libs/backend-apisix-standalone/test/operator.spec.ts b/libs/backend-apisix-standalone/test/operator.spec.ts new file mode 100644 index 00000000..fc1a6658 --- /dev/null +++ b/libs/backend-apisix-standalone/test/operator.spec.ts @@ -0,0 +1,46 @@ +import type * as ADCSDK from '@api7/adc-sdk'; +import type { AxiosInstance } from 'axios'; +import { Subject } from 'rxjs'; +import { SemVer } from 'semver'; + +import { Operator } from '../src/operator'; +import type * as typing from '../src/typing'; + +describe('Operator', () => { + const newOperator = () => + new Operator({ + cacheKey: 'test', + client: {} as AxiosInstance, + serverTokenMap: new Map(), + version: new SemVer('3.9.0'), + eventSubject: new Subject(), + oldRawConfiguration: {}, + }); + + it('should map ADC http_req_headers back to active health check req_headers', () => { + const operator = newOperator() as unknown as { + fromADCUpstream: ( + res: ADCSDK.Upstream, + parentId?: string, + ) => typing.Upstream; + }; + + const result = operator.fromADCUpstream({ + checks: { + active: { + type: 'http', + http_req_headers: ['X-Foo: bar'], + http_req_body: 'ping', + }, + }, + } as ADCSDK.Upstream); + + expect(result.checks).toEqual({ + active: { + type: 'http', + req_headers: ['X-Foo: bar'], + http_req_body: 'ping', + }, + }); + }); +}); diff --git a/libs/backend-apisix-standalone/test/transformer.spec.ts b/libs/backend-apisix-standalone/test/transformer.spec.ts new file mode 100644 index 00000000..46ae8eeb --- /dev/null +++ b/libs/backend-apisix-standalone/test/transformer.spec.ts @@ -0,0 +1,41 @@ +import { toADC } from '../src/transformer'; +import type * as typing from '../src/typing'; + +describe('Transformer', () => { + it('should map active health check req_headers to ADC http_req_headers', () => { + const input: typing.APISIXStandalone = { + services: [ + { + modifiedIndex: 1, + id: 'svc-1', + name: 'svc-1', + upstream_id: 'ups-1', + }, + ], + upstreams: [ + { + modifiedIndex: 1, + id: 'ups-1', + name: 'ups-1', + checks: { + active: { + type: 'http', + req_headers: ['X-Foo: bar'], + http_req_body: 'ping', + }, + }, + }, + ], + }; + + const result = toADC(input); + + expect(result.services?.[0].upstream?.checks).toEqual({ + active: { + type: 'http', + http_req_headers: ['X-Foo: bar'], + http_req_body: 'ping', + }, + }); + }); +}); diff --git a/libs/backend-apisix/src/transformer.ts b/libs/backend-apisix/src/transformer.ts index 192827e1..91b883a6 100644 --- a/libs/backend-apisix/src/transformer.ts +++ b/libs/backend-apisix/src/transformer.ts @@ -211,7 +211,28 @@ export class ToADC { type: upstream.type, hash_on: upstream.hash_on, key: upstream.key, - checks: upstream.checks, + // APISIX Admin API's "req_headers" maps to ADC's "http_req_headers"; + // every other active health check field is named the same. + checks: upstream.checks + ? { + active: { + type: upstream.checks.active.type, + timeout: upstream.checks.active.timeout, + concurrency: upstream.checks.active.concurrency, + host: upstream.checks.active.host, + port: upstream.checks.active.port, + http_method: upstream.checks.active.http_method, + http_path: upstream.checks.active.http_path, + http_req_headers: upstream.checks.active.req_headers, + http_req_body: upstream.checks.active.http_req_body, + https_verify_certificate: + upstream.checks.active.https_verify_certificate, + healthy: upstream.checks.active.healthy, + unhealthy: upstream.checks.active.unhealthy, + }, + passive: upstream.checks.passive, + } + : undefined, nodes, scheme: upstream.scheme, retries: upstream.retries, @@ -418,6 +439,28 @@ export class FromADC { return ADCSDK.utils.recursiveOmitUndefined>({ ...omit(upstream, 'id'), labels: FromADC.transformLabels(upstream.labels), + // ADC's "http_req_headers" maps to APISIX Admin API's "req_headers"; + // every other active health check field is named the same. + checks: upstream.checks + ? { + active: { + type: upstream.checks.active.type, + timeout: upstream.checks.active.timeout, + concurrency: upstream.checks.active.concurrency, + host: upstream.checks.active.host, + port: upstream.checks.active.port, + http_method: upstream.checks.active.http_method, + http_path: upstream.checks.active.http_path, + req_headers: upstream.checks.active.http_req_headers, + http_req_body: upstream.checks.active.http_req_body, + https_verify_certificate: + upstream.checks.active.https_verify_certificate, + healthy: upstream.checks.active.healthy, + unhealthy: upstream.checks.active.unhealthy, + }, + passive: upstream.checks.passive, + } + : undefined, }); } } diff --git a/libs/backend-apisix/src/typing.ts b/libs/backend-apisix/src/typing.ts index 375d923a..0339eb76 100644 --- a/libs/backend-apisix/src/typing.ts +++ b/libs/backend-apisix/src/typing.ts @@ -6,7 +6,7 @@ import { Plugins, ResourceType, UpstreamBalancer, - UpstreamHealthCheck, + UpstreamHealthCheck as ADCUpstreamHealthCheck, UpstreamNode, UpstreamPassHost, UpstreamScheme, @@ -15,6 +15,18 @@ import { export const ADC_UPSTREAM_SERVICE_ID_LABEL = '__ADC_UPSTREAM_SERVICE_ID'; +// APISIX Admin API uses "req_headers" on the wire, while ADC exposes it as +// "http_req_headers"; every other active health check field is named the same. +export type UpstreamHealthCheckActive = Omit< + ADCUpstreamHealthCheck['active'], + 'http_req_headers' +> & { + req_headers?: Array; +}; +export type UpstreamHealthCheck = Omit & { + active: UpstreamHealthCheckActive; +}; + export interface Route { id: string; name?: string; diff --git a/libs/backend-apisix/test/transformer.spec.ts b/libs/backend-apisix/test/transformer.spec.ts index bbd59f49..86ba0e4c 100644 --- a/libs/backend-apisix/test/transformer.spec.ts +++ b/libs/backend-apisix/test/transformer.spec.ts @@ -1,4 +1,4 @@ -import { ToADC } from '../src/transformer'; +import { FromADC, ToADC } from '../src/transformer'; describe('Transformer', () => { it('should transform upstream nodes to array', () => { @@ -11,4 +11,50 @@ describe('Transformer', () => { }), ).toEqual({ nodes: [{ host: '127.0.0.1', port: 5432, weight: 100 }] }); }); + + it('should map active health check req_headers to ADC http_req_headers', () => { + const toADC = new ToADC(); + expect( + toADC.transformUpstream({ + checks: { + active: { + type: 'http', + req_headers: ['X-Foo: bar'], + http_req_body: 'ping', + }, + }, + }), + ).toEqual({ + checks: { + active: { + type: 'http', + http_req_headers: ['X-Foo: bar'], + http_req_body: 'ping', + }, + }, + }); + }); + + it('should map ADC http_req_headers back to active health check req_headers', () => { + const fromADC = new FromADC(); + expect( + fromADC.transformUpstream({ + checks: { + active: { + type: 'http', + http_req_headers: ['X-Foo: bar'], + http_req_body: 'ping', + }, + }, + }), + ).toEqual({ + checks: { + active: { + type: 'http', + req_headers: ['X-Foo: bar'], + http_req_body: 'ping', + }, + }, + }); + }); }); diff --git a/libs/converter-openapi/package.json b/libs/converter-openapi/package.json index f30bb0cc..995d054b 100644 --- a/libs/converter-openapi/package.json +++ b/libs/converter-openapi/package.json @@ -21,8 +21,8 @@ }, "dependencies": { "@api7/adc-sdk": "workspace:*", - "@scalar/openapi-parser": "0.28.8", - "@scalar/openapi-types": "^0.9.1", + "@scalar/openapi-parser": "0.28.11", + "@scalar/openapi-types": "0.9.4", "slugify": "^1.6.6", "lodash-es": "catalog:", "rxjs": "catalog:", diff --git a/libs/sdk/src/core/schema.ts b/libs/sdk/src/core/schema.ts index 4d6df219..1faffa14 100644 --- a/libs/sdk/src/core/schema.ts +++ b/libs/sdk/src/core/schema.ts @@ -2,8 +2,8 @@ import { isNil } from 'lodash-es'; import type { ZodRawShape } from 'zod'; import { z } from 'zod'; -import { FieldListType } from './resource'; import { withDifferMeta } from './field-registry'; +import { FieldListType } from './resource'; const idSchema = z .string() @@ -40,6 +40,18 @@ export type UpstreamHealthCheck = NonNullable< export type UpstreamTimeout = z.infer; const hostSchema = z.string().min(1); const portSchema = z.coerce.number().int().min(1).max(65535); +const methodSchema = z.enum([ + 'GET', + 'POST', + 'PUT', + 'DELETE', + 'PATCH', + 'HEAD', + 'OPTIONS', + 'CONNECT', + 'TRACE', + 'PURGE', +]); const secretRefSchema = z.string().regex(/^\$(secret|env):\/\//); const certificateSchema = z.union( [ @@ -121,9 +133,11 @@ const upstreamSchema = (extend?: ZodRawShape) => concurrency: z.coerce.number().default(10).optional(), host: hostSchema.optional(), port: portSchema.optional(), + http_method: methodSchema.default('GET').optional(), http_path: z.string().default('/').optional(), + http_req_headers: z.array(z.string()).min(1).optional(), + http_req_body: z.string().default('').optional(), https_verify_certificate: z.boolean().default(true).optional(), - http_request_headers: z.array(z.string()).min(1).optional(), healthy: z .strictObject({ ...upstreamHealthCheckPassiveHealthy.shape, @@ -205,23 +219,7 @@ const routeSchema = z.strictObject({ priority: z.coerce.number().int().optional(), timeout: timeoutSchema.optional(), vars: exprSchema.optional(), - methods: z - .array( - z.enum([ - 'GET', - 'POST', - 'PUT', - 'DELETE', - 'PATCH', - 'HEAD', - 'OPTIONS', - 'CONNECT', - 'TRACE', - 'PURGE', - ]), - ) - .nonempty() - .optional(), + methods: z.array(methodSchema).nonempty().optional(), enable_websocket: z.boolean().optional(), remote_addrs: z .array( @@ -230,7 +228,9 @@ const routeSchema = z.strictObject({ }), ) .optional(), - plugins: withDifferMeta(pluginsSchema.optional(), { listType: FieldListType.OBJECT_MAP }), + plugins: withDifferMeta(pluginsSchema.optional(), { + listType: FieldListType.OBJECT_MAP, + }), filter_func: z.string().optional(), }); export type Route = z.infer; @@ -242,7 +242,9 @@ const streamRouteSchema = z.strictObject({ description: descriptionSchema.optional(), labels: labelsSchema.optional(), - plugins: withDifferMeta(pluginsSchema.optional(), { listType: FieldListType.OBJECT_MAP }), + plugins: withDifferMeta(pluginsSchema.optional(), { + listType: FieldListType.OBJECT_MAP, + }), remote_addr: z.string().optional(), server_addr: z.string().optional(), @@ -260,19 +262,28 @@ export const serviceBaseSchema = z.strictObject({ upstream: upstreamSchema().optional(), upstreams: withDifferMeta( - z.array(upstreamSchema({ id: idSchema.optional(), name: z.string() })).optional(), + z + .array(upstreamSchema({ id: idSchema.optional(), name: z.string() })) + .optional(), { listType: FieldListType.MAP, listMapKey: 'name', nested: true }, ), - plugins: withDifferMeta(pluginsSchema.optional(), { listType: FieldListType.OBJECT_MAP }), + plugins: withDifferMeta(pluginsSchema.optional(), { + listType: FieldListType.OBJECT_MAP, + }), path_prefix: z.string().optional(), strip_path_prefix: z.boolean().optional(), hosts: z.array(hostSchema).optional(), - routes: withDifferMeta(z.array(routeSchema).optional(), { listType: FieldListType.MAP, listMapKey: 'name', nested: true }), - stream_routes: withDifferMeta( - z.array(streamRouteSchema).optional(), - { listType: FieldListType.MAP, listMapKey: 'name', nested: true }, - ), + routes: withDifferMeta(z.array(routeSchema).optional(), { + listType: FieldListType.MAP, + listMapKey: 'name', + nested: true, + }), + stream_routes: withDifferMeta(z.array(streamRouteSchema).optional(), { + listType: FieldListType.MAP, + listMapKey: 'name', + nested: true, + }), }); const serviceSchema = serviceBaseSchema .refine( @@ -346,11 +357,15 @@ export const consumerSchema = z.strictObject({ description: descriptionSchema.optional(), labels: labelsSchema.optional(), - plugins: withDifferMeta(pluginsSchema.optional(), { listType: FieldListType.OBJECT_MAP }), - credentials: withDifferMeta( - z.array(consumerCredentialSchema).optional(), - { listType: FieldListType.MAP, listMapKey: 'name', nested: true, configKey: 'consumer_credentials' }, - ), + plugins: withDifferMeta(pluginsSchema.optional(), { + listType: FieldListType.OBJECT_MAP, + }), + credentials: withDifferMeta(z.array(consumerCredentialSchema).optional(), { + listType: FieldListType.MAP, + listMapKey: 'name', + nested: true, + configKey: 'consumer_credentials', + }), }); export type Consumer = z.infer; @@ -360,12 +375,15 @@ export const consumerGroupSchema = z.strictObject({ description: descriptionSchema.optional(), labels: labelsSchema.optional(), - plugins: withDifferMeta(pluginsSchema.optional(), { listType: FieldListType.OBJECT_MAP }), + plugins: withDifferMeta(pluginsSchema.optional(), { + listType: FieldListType.OBJECT_MAP, + }), - consumers: withDifferMeta( - z.array(consumerSchema).optional(), - { listType: FieldListType.MAP, listMapKey: 'username', nested: true }, - ), + consumers: withDifferMeta(z.array(consumerSchema).optional(), { + listType: FieldListType.MAP, + listMapKey: 'username', + nested: true, + }), }); export type ConsumerGroup = z.infer; diff --git a/package.json b/package.json index 880bab00..b6c4ab6a 100644 --- a/package.json +++ b/package.json @@ -31,5 +31,5 @@ "typescript-eslint": "^8.61.0", "vitest": "catalog:" }, - "packageManager": "pnpm@11.17.0+sha512.cca3cea332ad254bb84145f966d19f4879615210346fc92c79a047f23a0d7b3cca3c3792f0076ba1f1831d277efbcf0a9119b31a9a60eca7fb3d6231f331ef72" + "packageManager": "pnpm@11.20.0+sha512.9a6f330a95b66446ea088faf1521405a8a01f07fde7124cc9958dfed52d4bb436737e65b08f85f37b46fcba375092558ac51262b816844b22f63406ed166bfee" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 80c6bb85..a657bd2a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -60,10 +60,10 @@ overrides: path-to-regexp: '>=8.4.0' body-parser: '>=2.2.1' qs: '>=6.14.2' - fast-uri: '>=4.1.1' + fast-uri: '>=4.1.2' postcss: '>=8.5.18' - minimatch@10>brace-expansion: 5.0.8 - nx>brace-expansion: 5.0.8 + minimatch@10>brace-expansion: 5.0.9 + nx>brace-expansion: 5.0.9 '@trivago/prettier-plugin-sort-imports>minimatch': ^10.0.0 importers: @@ -387,11 +387,11 @@ importers: specifier: workspace:* version: link:../sdk '@scalar/openapi-parser': - specifier: 0.28.8 - version: 0.28.8 + specifier: 0.28.11 + version: 0.28.11 '@scalar/openapi-types': - specifier: ^0.9.1 - version: 0.9.1 + specifier: 0.9.4 + version: 0.9.4 lodash-es: specifier: '>=4.17.24' version: 4.18.1 @@ -1665,24 +1665,24 @@ packages: '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} - '@scalar/helpers@0.9.0': - resolution: {integrity: sha512-M34CLRCttqC1bXthI/QSzQj0s5C6nrU2PFWf/vOT3RpycbiGDGQbqR+5RfFzpOIQvRqbHfNdcRbeiZBw+vCbkQ==} + '@scalar/helpers@0.9.2': + resolution: {integrity: sha512-hjyMpMZjTBZQhyByZmz5oUgRKUQJO5V5AOiJxsVEGbUmgA7sJRQeTrXLB+BEwzaKS5nm2opJeNyMBYLFNK4hiQ==} engines: {node: '>=22'} - '@scalar/json-magic@0.12.17': - resolution: {integrity: sha512-Vw2nrUDIjhvMP6vxFtkiiFlabJ6SyTtfn1BsOxgnr1hIB+/rkngMguiDzl5em21VjyfFGIoADia+QWKM2hdcdA==} + '@scalar/json-magic@0.12.19': + resolution: {integrity: sha512-1T4QoFYZ1nKt25xFeHtghAuZzaLq2X4CpCSLFXG0Fjcz6K2HIZqo+RtywfI0WD8RRRRgS34keo7X4Gv1BQUNoQ==} engines: {node: '>=22'} - '@scalar/openapi-parser@0.28.8': - resolution: {integrity: sha512-BO98D3TLfKNL80UnE1sIAmI6DQRmOaH0AHnlAooTn1sQjNbRDaeHyRO53EEjo7HjFEdHeQtiRzoXmMB4jvt8AA==} + '@scalar/openapi-parser@0.28.11': + resolution: {integrity: sha512-veUkTrLbfT4mqsf3jtnLi8SnsQqXQsBnvyBzZzmu0NohyAIvSjFaYsTeWXx9frFrQSXyZGHIsaFJ9Dtrupf/0Q==} engines: {node: '>=22'} - '@scalar/openapi-types@0.9.1': - resolution: {integrity: sha512-gkGhSkxSzADaBiNg+ZAbJuwj+ZUmzP2Pg9CWZ7ZP+0fck2WjPeDDM7aAbouAm0aQQMF9xBjSPXSA9a/qTHYaTw==} + '@scalar/openapi-types@0.9.4': + resolution: {integrity: sha512-eUSIjZEBLEF2i2pvcNdzXhzlKx6qt+hZsNklLmCyzPBoXWxHcQaEClgMFavXDRRvJDdi6LkyjqGUqqf0XgRgFg==} engines: {node: '>=22'} - '@scalar/openapi-upgrader@0.2.9': - resolution: {integrity: sha512-D5b0rGLLZgmkO9mdW2j/ND1KBlH1u3RCpr87HPxv9P9ZSr6PtM5iLqFOJq0ACiaHjY2mikCrxgDmnUEhTzRpHQ==} + '@scalar/openapi-upgrader@0.2.12': + resolution: {integrity: sha512-oj8O79xVDCHx759owTknwyBAYynaDQ8NGa5wIHduEUAFCob4YnsMEaFDpsconzogchH/whOGzg63BXHST8XPAw==} engines: {node: '>=22'} '@so-ric/colorspace@1.1.6': @@ -2183,8 +2183,8 @@ packages: resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} engines: {node: '>=18'} - brace-expansion@5.0.8: - resolution: {integrity: sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==} + brace-expansion@5.0.9: + resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} engines: {node: 20 || >=22} browserslist@4.28.1: @@ -2569,8 +2569,8 @@ packages: fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - fast-uri@4.1.1: - resolution: {integrity: sha512-YPOs1zD5TG2+EZt+r88LwF6mclA7TPkpwMP7ZN3TO2HiHS8TXvq7QA/17iJsV9dubcLo/f8eEYqMBruyQV21hQ==} + fast-uri@4.1.2: + resolution: {integrity: sha512-TyGmBcbDTZXcb2cj5MV89DrF42DKvb3y5DDUNh95iO+IMeAzMkVSxK1PZRrRIpc9yg8U2GhGdbofNa0LS/a4Bw==} fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} @@ -5088,20 +5088,20 @@ snapshots: '@rolldown/pluginutils@1.0.1': {} - '@scalar/helpers@0.9.0': {} + '@scalar/helpers@0.9.2': {} - '@scalar/json-magic@0.12.17': + '@scalar/json-magic@0.12.19': dependencies: - '@scalar/helpers': 0.9.0 + '@scalar/helpers': 0.9.2 pathe: 2.0.3 yaml: 2.9.0 - '@scalar/openapi-parser@0.28.8': + '@scalar/openapi-parser@0.28.11': dependencies: - '@scalar/helpers': 0.9.0 - '@scalar/json-magic': 0.12.17 - '@scalar/openapi-types': 0.9.1 - '@scalar/openapi-upgrader': 0.2.9 + '@scalar/helpers': 0.9.2 + '@scalar/json-magic': 0.12.19 + '@scalar/openapi-types': 0.9.4 + '@scalar/openapi-upgrader': 0.2.12 ajv: 8.20.0 ajv-draft-04: 1.0.0(ajv@8.20.0) ajv-formats: 3.0.1 @@ -5109,11 +5109,11 @@ snapshots: leven: 4.0.0 yaml: 2.9.0 - '@scalar/openapi-types@0.9.1': {} + '@scalar/openapi-types@0.9.4': {} - '@scalar/openapi-upgrader@0.2.9': + '@scalar/openapi-upgrader@0.2.12': dependencies: - '@scalar/openapi-types': 0.9.1 + '@scalar/openapi-types': 0.9.4 '@so-ric/colorspace@1.1.6': dependencies: @@ -5543,7 +5543,7 @@ snapshots: ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 4.1.1 + fast-uri: 4.1.2 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -5675,7 +5675,7 @@ snapshots: transitivePeerDependencies: - supports-color - brace-expansion@5.0.8: + brace-expansion@5.0.9: dependencies: balanced-match: 4.0.4 @@ -6086,7 +6086,7 @@ snapshots: fast-safe-stringify@2.1.1: {} - fast-uri@4.1.1: {} + fast-uri@4.1.2: {} fdir@6.5.0(picomatch@4.0.5): optionalDependencies: @@ -6522,7 +6522,7 @@ snapshots: minimatch@10.2.5: dependencies: - brace-expansion: 5.0.8 + brace-expansion: 5.0.9 minimist@1.2.8: {} @@ -6564,7 +6564,7 @@ snapshots: balanced-match: 4.0.3 base64-js: 1.5.1 bl: 4.1.0 - brace-expansion: 5.0.8 + brace-expansion: 5.0.9 buffer: 5.7.1 call-bind-apply-helpers: 1.0.2 chalk: 4.1.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index d95e72d9..e37bb9e6 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -40,13 +40,13 @@ overrides: path-to-regexp: '>=8.4.0' body-parser: '>=2.2.1' qs: '>=6.14.2' - fast-uri: '>=4.1.1' + fast-uri: '>=4.1.2' postcss: '>=8.5.18' - 'minimatch@10>brace-expansion': '5.0.8' - 'nx>brace-expansion': '5.0.8' + 'minimatch@10>brace-expansion': '5.0.9' + 'nx>brace-expansion': '5.0.9' # @trivago/prettier-plugin-sort-imports pins minimatch@^9, which drags in the # unpatched brace-expansion@2.x line (GHSA-mh99-v99m-4gvg). Force it onto minimatch@10 - # instead, which already resolves to the patched brace-expansion@5.0.8. + # instead, which already resolves to the patched brace-expansion@5.0.9. '@trivago/prettier-plugin-sort-imports>minimatch': '^10.0.0' strictPeerDependencies: false diff --git a/schema.json b/schema.json index d4e53a22..e442efc2 100644 --- a/schema.json +++ b/schema.json @@ -120,21 +120,41 @@ "minimum": 1, "maximum": 65535 }, + "http_method": { + "default": "GET", + "type": "string", + "enum": [ + "GET", + "POST", + "PUT", + "DELETE", + "PATCH", + "HEAD", + "OPTIONS", + "CONNECT", + "TRACE", + "PURGE" + ] + }, "http_path": { "default": "/", "type": "string" }, - "https_verify_certificate": { - "default": true, - "type": "boolean" - }, - "http_request_headers": { + "http_req_headers": { "minItems": 1, "type": "array", "items": { "type": "string" } }, + "http_req_body": { + "default": "", + "type": "string" + }, + "https_verify_certificate": { + "default": true, + "type": "boolean" + }, "healthy": { "type": "object", "properties": { @@ -552,21 +572,41 @@ "minimum": 1, "maximum": 65535 }, + "http_method": { + "default": "GET", + "type": "string", + "enum": [ + "GET", + "POST", + "PUT", + "DELETE", + "PATCH", + "HEAD", + "OPTIONS", + "CONNECT", + "TRACE", + "PURGE" + ] + }, "http_path": { "default": "/", "type": "string" }, - "https_verify_certificate": { - "default": true, - "type": "boolean" - }, - "http_request_headers": { + "http_req_headers": { "minItems": 1, "type": "array", "items": { "type": "string" } }, + "http_req_body": { + "default": "", + "type": "string" + }, + "https_verify_certificate": { + "default": true, + "type": "boolean" + }, "healthy": { "type": "object", "properties": {