From 0308c5bfa352efc04e8d46a96e80ef0b55e4bcf6 Mon Sep 17 00:00:00 2001 From: arvinxx Date: Fri, 15 Dec 2023 23:18:46 +0800 Subject: [PATCH] :bug: fix: fix schema required --- src/openapi/index.ts | 27 +++++++- tests/__snapshots__/openapi.test.ts.snap | 54 +++++++++++++++ tests/fixtures/ChatWithPDF.json | 87 ++++++++++++++++++++++++ tests/openapi.test.ts | 8 +++ 4 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 tests/fixtures/ChatWithPDF.json diff --git a/src/openapi/index.ts b/src/openapi/index.ts index 32d2e2d..75486d3 100644 --- a/src/openapi/index.ts +++ b/src/openapi/index.ts @@ -31,7 +31,6 @@ export class OpenAPIConvertor { const parameters = this.mergeSchemas(...Object.values(parametersSchema)); if (requestBodySchema && Object.keys(requestBodySchema.properties).length > 0) { - console.log(requestBodySchema); parameters.properties[OPENAPI_REQUEST_BODY_KEY] = requestBodySchema; parameters.required?.push('_requestBody'); } @@ -168,7 +167,7 @@ export class OpenAPIConvertor { for (const [contentType, mediaType] of Object.entries(requestBody.content)) { if (mediaType.schema) { // 直接使用已解析的 Schema - const resolvedSchema = mediaType.schema; + const resolvedSchema = this.removeRequiredFields(mediaType.schema); // 根据不同的 content-type,可以在这里添加特定的处理逻辑 switch (contentType) { @@ -196,6 +195,30 @@ export class OpenAPIConvertor { return requestBodySchema as PluginSchema; } + private removeRequiredFields(schema: any): any { + if (schema && typeof schema === 'object') { + // 如果是对象类型,遍历它的每个属性 + for (const key in schema) { + // eslint-disable-next-line no-prototype-builtins + if (schema.hasOwnProperty(key)) { + const value = schema[key]; + + // 如果属性是 required 并且值为 true,则删除该属性 + if (key === 'required' && value === true) { + delete schema[key]; + } else { + // 否则,如果属性是对象或数组,则递归处理 + schema[key] = this.removeRequiredFields(value); + } + } + } + } else if (Array.isArray(schema)) { + // 如果是数组类型,遍历每个元素 + return schema.map(this.removeRequiredFields.bind(this)); + } + return schema; + } + private mergeSchemas(...schemas: any[]) { // 初始化合并后的 Schema const mergedSchema: PluginSchema = { diff --git a/tests/__snapshots__/openapi.test.ts.snap b/tests/__snapshots__/openapi.test.ts.snap index 9ae8bfc..ec0e165 100644 --- a/tests/__snapshots__/openapi.test.ts.snap +++ b/tests/__snapshots__/openapi.test.ts.snap @@ -1,5 +1,59 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`OpenAPIConvertor > convertOpenAPIToPluginSchema > ChatWithPDF 1`] = ` +[ + { + "description": "Load a PDF document", + "name": "loadPdf", + "parameters": { + "properties": { + "_requestBody": { + "properties": { + "pdf_url": { + "description": "The temporary URL of the PDF document to load.", + "format": "uri", + "type": "string", + }, + }, + "required": [ + "pdf_url", + ], + "type": "object", + }, + }, + "type": "object", + }, + }, + { + "description": "Query a loaded PDF document", + "name": "queryPdf", + "parameters": { + "properties": { + "_requestBody": { + "properties": { + "pdf_url": { + "description": "The temporary URL of the PDF document that is already loaded.", + "format": "uri", + "type": "string", + }, + "query": { + "description": "The query or question to ask based on the PDF document.", + "type": "string", + }, + }, + "required": [ + "query", + "pdf_url", + ], + "type": "object", + }, + }, + "type": "object", + }, + }, +] +`; + exports[`OpenAPIConvertor > convertOpenAPIToPluginSchema > can convert OpenAPI v2 MJ openAPI 1`] = ` [ { diff --git a/tests/fixtures/ChatWithPDF.json b/tests/fixtures/ChatWithPDF.json new file mode 100644 index 0000000..d5a2acc --- /dev/null +++ b/tests/fixtures/ChatWithPDF.json @@ -0,0 +1,87 @@ +{ + "components": { + "schemas": { + "loadPdfRequest": { + "type": "object", + "required": ["pdf_url"], + "properties": { + "pdf_url": { + "type": "string", + "format": "uri", + "description": "The temporary URL of the PDF document to load.", + "required": true + } + } + }, + "queryPdfRequest": { + "type": "object", + "required": ["query", "pdf_url"], + "properties": { + "query": { + "type": "string", + "description": "The query or question to ask based on the PDF document.", + "required": true + }, + "pdf_url": { + "type": "string", + "format": "uri", + "description": "The temporary URL of the PDF document that is already loaded.", + "required": true + } + } + }, + "queryPdfResponse": { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { "type": "string" }, + "description": "The list of text chunks from the PDF document that are relevant to the user's query." + } + } + } + } + }, + "info": { + "title": "ChatWithPDF", + "description": "A plugin that allows users to load and query PDF documents or Google Drive documents using ChatGPT. Users must first provide a PDF URL for processing. Once the PDF is loaded, users can query, analyze, or ask questions from that PDF name without needing to specify everytime. User must provide a PDF or Google Drive link that can be publically accessible, only documents can be loaded. The query will be able to extract relevant parts of the document to the users request. The load may take a while to process and if it does not work on the first try, try again, unless you get an error message back. User can only load documents that can be publically accessible on the internet. If they wish to use Google Docs they must first export it as a PDF, upload it to Google Drive then share a link that anybody can access via the link so we can download and process it. And if they wish to upload their document they can instead use service like [Upload Document](https://tmpfiles.org/).", + "version": "v3" + }, + "openapi": "3.0.1", + "paths": { + "/pdf/load": { + "post": { + "operationId": "loadPdf", + "summary": "Load a PDF document", + "requestBody": { + "required": true, + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/loadPdfRequest" } } + } + }, + "responses": { "200": { "description": "OK" } } + } + }, + "/pdf/query": { + "post": { + "operationId": "queryPdf", + "summary": "Query a loaded PDF document", + "requestBody": { + "required": true, + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/queryPdfRequest" } } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/queryPdfResponse" } } + } + } + } + } + } + }, + "servers": [{ "url": "https://chatwithpdf.sdan.io" }] +} diff --git a/tests/openapi.test.ts b/tests/openapi.test.ts index 65f6094..0221998 100644 --- a/tests/openapi.test.ts +++ b/tests/openapi.test.ts @@ -1,6 +1,7 @@ import { OpenAPIConvertor } from '@lobehub/chat-plugin-sdk/openapi'; import { describe, expect, it } from 'vitest'; +import ChatWithPDF from './fixtures/ChatWithPDF.json'; import OpenAPI_Auth_API_Key from './fixtures/OpenAPI_Auth_API_Key.json'; import OpenAPIV2 from './fixtures/OpenAPI_V2.json'; import openAPIV3 from './fixtures/OpenAPI_V3.json'; @@ -28,6 +29,13 @@ describe('OpenAPIConvertor', () => { expect(plugins).toMatchSnapshot(); }); + + it('ChatWithPDF', async () => { + const convertor = new OpenAPIConvertor(ChatWithPDF); + const plugins = await convertor.convertOpenAPIToPluginSchema(); + + expect(plugins).toMatchSnapshot(); + }); }); describe('convertAuthToSettingsSchema', () => {