diff --git a/scripts/testCodeGenWithFunctional.ts b/scripts/testCodeGenWithFunctional.ts index b03baf8d..476749ad 100644 --- a/scripts/testCodeGenWithFunctional.ts +++ b/scripts/testCodeGenWithFunctional.ts @@ -81,6 +81,10 @@ const main = () => { Writer.generateFormatTypeCode("test/format.domain/index.yml", "test/code/functional/format.domain/code.ts"); Writer.generateFormatTypeCode("test/cloudflare/openapi.yaml", "test/code/functional/cloudflare/client.ts"); + + Writer.generateTypedefWithTemplateCode("test/example-validation/index.yml", "test/code/functional/example-validation/client.ts", true, { + sync: false, + }); }; main(); diff --git a/src/internal/Validator/__tests__/index.test.ts b/src/internal/Validator/__tests__/index.test.ts new file mode 100644 index 00000000..8ca10eb1 --- /dev/null +++ b/src/internal/Validator/__tests__/index.test.ts @@ -0,0 +1,87 @@ +import { describe, expect, test } from "vitest"; +import type * as Types from "../../../types"; +import { validate } from "../index"; + +const createDocument = (operation: Record): Types.OpenApi.Document => { + return { + openapi: "3.0.0", + info: { + title: "Validator test", + version: "1.0.0", + }, + paths: { + "/chat": { + get: operation, + }, + }, + } as unknown as Types.OpenApi.Document; +}; + +describe("validate", () => { + test.each([ + [ + "Parameter Object", + createDocument({ + parameters: [ + { + name: "message", + in: "query", + schema: { type: "string" }, + example: "hello", + }, + ], + responses: { "200": { description: "OK" } }, + }), + ], + [ + "Media Type Object", + createDocument({ + responses: { + "200": { + description: "SSE stream", + content: { + "text/event-stream": { + schema: { type: "string" }, + example: "event: message\\ndata: hello", + }, + }, + }, + }, + }), + ], + [ + "Header Object", + createDocument({ + responses: { + "200": { + description: "OK", + headers: { + "X-Has-More": { + schema: { type: "boolean" }, + example: false, + }, + }, + }, + }, + }), + ], + [ + "Link Object", + createDocument({ + responses: { + "200": { + description: "OK", + links: { + next: { + operationId: "getChat", + parameters: { message: "hello" }, + }, + }, + }, + }, + }), + ], + ])("%s の example-like value accepts non-object values", (_name, document) => { + expect(() => validate(document)).not.toThrow(); + }); +}); diff --git a/src/internal/Validator/openapi.json b/src/internal/Validator/openapi.json index d910cf23..30eb469f 100644 --- a/src/internal/Validator/openapi.json +++ b/src/internal/Validator/openapi.json @@ -414,7 +414,7 @@ "$ref": "#/definitions/schemaOrReference" }, "example": { - "$ref": "#/definitions/any" + "$ref": "#/definitions/defaultType" }, "examples": { "$ref": "#/definitions/examplesOrReferences" @@ -460,7 +460,7 @@ "$ref": "#/definitions/schemaOrReference" }, "example": { - "$ref": "#/definitions/any" + "$ref": "#/definitions/defaultType" }, "examples": { "$ref": "#/definitions/examplesOrReferences" @@ -642,7 +642,7 @@ "$ref": "#/definitions/schemaOrReference" }, "example": { - "$ref": "#/definitions/any" + "$ref": "#/definitions/defaultType" }, "examples": { "$ref": "#/definitions/examplesOrReferences" @@ -1002,7 +1002,7 @@ "anyOrExpression": { "oneOf": [ { - "$ref": "#/definitions/any" + "$ref": "#/definitions/defaultType" }, { "$ref": "#/definitions/expression" diff --git a/test/__tests__/functional/__snapshots__/example-validation/client.ts b/test/__tests__/functional/__snapshots__/example-validation/client.ts new file mode 100644 index 00000000..8c2dc9a5 --- /dev/null +++ b/test/__tests__/functional/__snapshots__/example-validation/client.ts @@ -0,0 +1,74 @@ +// +// Generated by @himenon/openapi-typescript-code-generator +// +// OpenApi : 3.0.0 +// +// + + +export interface Parameter$getChat { + message?: string; +} +export interface Response$getChat$Status$200 { + "text/event-stream": string; +} +export type ResponseContentType$getChat = keyof Response$getChat$Status$200; +export interface Params$getChat { + parameter: Parameter$getChat; +} +export type HttpMethod = "GET" | "PUT" | "POST" | "DELETE" | "OPTIONS" | "HEAD" | "PATCH" | "TRACE"; +export interface ObjectLike { + [key: string]: any; +} +export interface QueryParameter { + value: any; + style?: "form" | "spaceDelimited" | "pipeDelimited" | "deepObject"; + explode: boolean; +} +export interface QueryParameters { + [key: string]: QueryParameter; +} +export type SuccessResponses = Response$getChat$Status$200; +export namespace ErrorResponse { + export type getChat = void; +} +export interface Encoding { + readonly contentType?: string; + headers?: Record; + readonly style?: "form" | "spaceDelimited" | "pipeDelimited" | "deepObject"; + readonly explode?: boolean; + readonly allowReserved?: boolean; +} +export interface RequestArgs { + readonly httpMethod: HttpMethod; + readonly url: string; + headers: ObjectLike | any; + requestBody?: ObjectLike | any; + requestBodyEncoding?: Record; + queryParameters?: QueryParameters | undefined; +} +export interface ApiClient { + request: (requestArgs: RequestArgs, options?: RequestOption) => Promise; +} +export const createClient = (apiClient: ApiClient, baseUrl: string) => { + const _baseUrl = baseUrl.replace(/\/$/, ""); + return { + getChat: (params: Params$getChat, option?: RequestOption): Promise => { + const url = _baseUrl + `/chat`; + const headers = { + Accept: "text/event-stream" + }; + const queryParameters: QueryParameters = { + message: { value: params.parameter.message, explode: false } + }; + return apiClient.request({ + httpMethod: "GET", + url, + headers, + queryParameters: queryParameters + }, option); + } + }; +}; +type ClientFunction = typeof createClient; +export type Client = ReturnType>; diff --git a/test/__tests__/functional/example-validation-test.ts b/test/__tests__/functional/example-validation-test.ts new file mode 100644 index 00000000..473ea8fb --- /dev/null +++ b/test/__tests__/functional/example-validation-test.ts @@ -0,0 +1,12 @@ +import * as fs from "node:fs"; +import { describe, expect, test } from "vitest"; + +import * as Utils from "../../utils"; + +describe("Example validation", () => { + test("client.ts", async () => { + const generateCode = fs.readFileSync("test/code/functional/example-validation/client.ts", { encoding: "utf-8" }); + const text = Utils.replaceVersionInfo(generateCode); + await expect(text).toMatchFileSnapshot("./__snapshots__/example-validation/client.ts"); + }); +}); diff --git a/test/example-validation/index.yml b/test/example-validation/index.yml new file mode 100644 index 00000000..76a8ea8d --- /dev/null +++ b/test/example-validation/index.yml @@ -0,0 +1,35 @@ +openapi: 3.0.0 +info: + title: Example validation + version: 1.0.0 +paths: + /chat: + get: + operationId: getChat + parameters: + - name: message + in: query + required: false + schema: + type: string + example: hello + responses: + '200': + description: SSE stream + headers: + X-Has-More: + schema: + type: boolean + example: false + content: + text/event-stream: + schema: + type: string + example: | + event: message + data: hello + links: + next: + operationId: getChat + parameters: + message: hello