diff --git a/CHANGELOG.md b/CHANGELOG.md index e0904a82..92b716a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [2.0.5](https://github.com/open-rpc/schema-utils-js/compare/2.0.4...2.0.5) (2024-10-08) + + +### Bug Fixes + +* add failing test around bug with dereffing and leaving .components around ([ffd6c84](https://github.com/open-rpc/schema-utils-js/commit/ffd6c84f9ef559b2546f5a7de138115fcc719041)) +* remove extra components ([39c2120](https://github.com/open-rpc/schema-utils-js/commit/39c2120d47d55d1fde042f634123f8cc57121cff)) + ## [2.0.4](https://github.com/open-rpc/schema-utils-js/compare/2.0.3...2.0.4) (2024-10-03) diff --git a/src/dereference-document.test.ts b/src/dereference-document.test.ts index 2ccc5101..d7894a04 100644 --- a/src/dereference-document.test.ts +++ b/src/dereference-document.test.ts @@ -1,9 +1,16 @@ import * as _fs from "fs-extra"; -import dereferenceDocument, { OpenRPCDocumentDereferencingError } from "./dereference-document"; +import dereferenceDocument, { + OpenRPCDocumentDereferencingError, +} from "./dereference-document"; import defaultResolver from "@json-schema-tools/reference-resolver"; -import { OpenrpcDocument, ContentDescriptorObject, JSONSchema, MethodObject } from "@open-rpc/meta-schema"; +import { + OpenrpcDocument, + ContentDescriptorObject, + JSONSchema, + MethodObject, +} from "@open-rpc/meta-schema"; import { JSONSchemaObject } from "@json-schema-tools/meta-schema"; - +import { JSONSchema7 } from "json-schema"; const workingDocument: OpenrpcDocument = { info: { @@ -15,7 +22,6 @@ const workingDocument: OpenrpcDocument = { }; describe("dereferenceDocument", () => { - it("doesnt explode", async () => { expect.assertions(1); const document = await dereferenceDocument(workingDocument); @@ -30,7 +36,11 @@ describe("dereferenceDocument", () => { version: "1", }, methods: [ - { name: "abc", params: [], result: { name: "cba", schema: { type: "number" } } } + { + name: "abc", + params: [], + result: { name: "cba", schema: { type: "number" } }, + }, ], openrpc: "1.0.0-rc1", }); @@ -47,34 +57,34 @@ describe("dereferenceDocument", () => { params: [], result: { name: "abcfoo", - schema: { type: "number" } - } - } + schema: { type: "number" }, + }, + }, }, components: { schemas: { bigOlBaz: { $ref: "#/components/schemas/bigOlFoo" }, - bigOlFoo: { title: "bigOlFoo", type: "string" } + bigOlFoo: { title: "bigOlFoo", type: "string" }, }, contentDescriptors: { bazerino: { name: "bazerino", - schema: { title: "bigBazerino", type: "number" } + schema: { title: "bigBazerino", type: "number" }, }, barf: { name: "barf", - schema: { $ref: "#/components/schemas/bigOlFoo" } - } + schema: { $ref: "#/components/schemas/bigOlFoo" }, + }, }, tags: { - foobydooby: { name: "foobydooby" } + foobydooby: { name: "foobydooby" }, }, errors: { - bigBadError: { code: 123, message: "123" } + bigBadError: { code: 123, message: "123" }, }, examples: { abcEx: { name: "abcEx", value: [123, 321] }, - cbaEx: { name: "cbaEx", value: "abc" } + cbaEx: { name: "cbaEx", value: "abc" }, }, examplePairingObjects: { testy: { @@ -82,37 +92,156 @@ describe("dereferenceDocument", () => { params: [{ $ref: "#/components/examples/abcEx" }], result: { $ref: "#/components/examples/cbaEx" }, }, - } - } + }, + }, }; testDoc.methods.push({ tags: [{ $ref: "#/components/tags/foobydooby" }], errors: [{ $ref: "#/components/errors/bigBadError" }], - examples: [ - { $ref: "#/components/examplePairingObjects/testy" } - ], + examples: [{ $ref: "#/components/examplePairingObjects/testy" }], name: "foo", params: [ { $ref: "#/components/contentDescriptors/bazerino" }, - { name: "blah blah", schema: { $ref: "#/components/schemas/bigOlBaz" } }, - { $ref: "#/components/contentDescriptors/barf" } + { + name: "blah blah", + schema: { $ref: "#/components/schemas/bigOlBaz" }, + }, + { $ref: "#/components/contentDescriptors/barf" }, ], result: { name: "fooResult", - schema: { $ref: "#/components/schemas/bigOlFoo" } - } + schema: { $ref: "#/components/schemas/bigOlFoo" }, + }, }); - testDoc.methods.push({ "$ref": "#/x-methods/foobar" }) + testDoc.methods.push({ $ref: "#/x-methods/foobar" }); const document = await dereferenceDocument(testDoc); const docMethods = document.methods as MethodObject[]; expect(docMethods).toBeDefined(); expect(docMethods[0]).toBeDefined(); expect(docMethods[0].params[0]).toBeDefined(); - expect((docMethods[0].params[0] as ContentDescriptorObject).name).toBe("bazerino"); + expect((docMethods[0].params[0] as ContentDescriptorObject).name).toBe( + "bazerino" + ); expect(docMethods[0].result).toBeDefined(); - expect(((docMethods[0].result as ContentDescriptorObject).schema as JSONSchemaObject).title).toBe("bigOlFoo"); - expect(((docMethods[0].params as ContentDescriptorObject[])[1].schema as JSONSchemaObject).title).toBe("bigOlFoo"); + expect( + ( + (docMethods[0].result as ContentDescriptorObject) + .schema as JSONSchemaObject + ).title + ).toBe("bigOlFoo"); + expect( + ( + (docMethods[0].params as ContentDescriptorObject[])[1] + .schema as JSONSchemaObject + ).title + ).toBe("bigOlFoo"); + }); + + it("derefs items", async () => { + expect.assertions(3); + const testDoc = { + openrpc: "1.2.4", + info: { + title: "Test ref", + version: "1.0.0", + }, + methods: [ + { + name: "wallet_createSession", + paramStructure: "by-name", + params: [ + ], + result: { + name: "wallet_createSessionResult", + schema: { + type: "object", + properties: { + sessionScopes: { + $ref: "#/components/schemas/SessionScopes", + }, + }, + }, + }, + examples: [ + ], + errors: [] + }, + ], + components: { + schemas: { + SessionScopes: { + type: "object", + patternProperties: { + "[-a-z0-9]{3,8}(:[-_a-zA-Z0-9]{1,32})?": { + $ref: "#/components/schemas/Scope", + }, + }, + }, + ScopeString: { + type: "string", + pattern: "[-a-z0-9]{3,8}(:[-_a-zA-Z0-9]{1,32})?", + }, + Scope: { + type: "object", + title: "Scope", + description: "Scope for a multi-chain connection", + additionalProperties: true, + required: ["notifications", "methods"], + properties: { + scopes: { + type: "array", + items: { + $ref: "#/components/schemas/ScopeString", + }, + }, + methods: { + description: + "Methods that the wallet must support in order to be used with this provider.", + type: "array", + items: { + type: "string", + }, + }, + notifications: { + description: + "Notifications that the wallet must support in order to be used with this provider.", + type: "array", + items: { + type: "string", + }, + }, + rpcEndpoints: { + description: "JSON-RPC endpoints for this namespace.", + type: "array", + items: { + type: "string", + format: "uri", + }, + }, + rpcDocuments: { + type: "array", + description: + "OpenRPC documents that define RPC methods in which to anchor the methods authorized in a CAIP-25 interaction.", + items: { + type: "string", + format: "uri", + }, + }, + }, + }, + }, + }, + }; + const document = await dereferenceDocument(testDoc as OpenrpcDocument); + const docMethods = document.methods as MethodObject[]; + expect(docMethods).toBeDefined(); + expect(docMethods[0]).toBeDefined(); + expect( + () => { + JSON.stringify(document) + } + ).not.toThrow(); }); it("interdependent refs", async () => { @@ -128,13 +257,13 @@ describe("dereferenceDocument", () => { name: "foo", params: [ { $ref: "#/components/contentDescriptors/bazerino" }, - { name: "leFoo", schema: { $ref: "#/components/schemas/fatFoo" } } + { name: "leFoo", schema: { $ref: "#/components/schemas/fatFoo" } }, ], result: { name: "fooResult", - schema: { $ref: "#/components/schemas/bigBar" } - } - } + schema: { $ref: "#/components/schemas/bigBar" }, + }, + }, ], components: { schemas: { @@ -144,26 +273,28 @@ describe("dereferenceDocument", () => { type: "object", properties: { fatFoo: { $ref: "#/components/schemas/fatFoo" }, - badBaz: { $ref: "#/components/schemas/badBaz" } - } + badBaz: { $ref: "#/components/schemas/badBaz" }, + }, }, - badBaz: { title: "badBaz", type: "number" } + badBaz: { title: "badBaz", type: "number" }, }, contentDescriptors: { bazerino: { name: "bazerino", - schema: { $ref: "#/components/schemas/badBaz" } - } - } - } + schema: { $ref: "#/components/schemas/badBaz" }, + }, + }, + }, } as OpenrpcDocument; const document = await dereferenceDocument(testDoc); expect(document.methods).toBeDefined(); expect(document.methods[0]).toBeDefined(); - const params = (document.methods[0] as MethodObject).params as ContentDescriptorObject[]; - const result = (document.methods[0] as MethodObject).result as ContentDescriptorObject; + const params = (document.methods[0] as MethodObject) + .params as ContentDescriptorObject[]; + const result = (document.methods[0] as MethodObject) + .result as ContentDescriptorObject; expect(params).toBeDefined(); expect(result).toBeDefined(); @@ -175,14 +306,15 @@ describe("dereferenceDocument", () => { expect(param1.name).toBe("leFoo"); expect((param1.schema as JSONSchemaObject).title).toBe("fatFoo"); expect((result.schema as JSONSchemaObject).title).toBe("bigBar"); - const resultProps = (result.schema as JSONSchemaObject).properties as { [k: string]: JSONSchema }; + const resultProps = (result.schema as JSONSchemaObject).properties as { + [k: string]: JSONSchema; + }; expect(resultProps).toBeDefined(); expect((resultProps.fatFoo as JSONSchemaObject).title).toBe("fatFoo"); expect((resultProps.badBaz as JSONSchemaObject).title).toBe("badBaz"); }); - it("throws when a json pointer is invalid", async () => { expect.assertions(1); @@ -197,14 +329,14 @@ describe("dereferenceDocument", () => { name: "foo", params: [], result: { - $ref: " " - } - } + $ref: " ", + }, + }, ], }; try { - await dereferenceDocument(testDoc as OpenrpcDocument) + await dereferenceDocument(testDoc as OpenrpcDocument); } catch (e) { expect(e).toBeInstanceOf(OpenRPCDocumentDereferencingError); } @@ -224,14 +356,14 @@ describe("dereferenceDocument", () => { name: "foo", params: [], result: { - $ref: "#/doesnt/exists" - } - } + $ref: "#/doesnt/exists", + }, + }, ], }; try { - await dereferenceDocument(testDoc as OpenrpcDocument) + await dereferenceDocument(testDoc as OpenrpcDocument); } catch (e) { expect(e).toBeInstanceOf(OpenRPCDocumentDereferencingError); } @@ -250,18 +382,18 @@ describe("dereferenceDocument", () => { { name: "foo", params: [], - result: { name: "foobar", schema: true } - } + result: { name: "foobar", schema: true }, + }, ], components: { schemas: { - foo: { $ref: "#/abc123" } - } - } + foo: { $ref: "#/abc123" }, + }, + }, }; try { - await dereferenceDocument(testDoc as OpenrpcDocument) + await dereferenceDocument(testDoc as OpenrpcDocument); } catch (e) { expect(e).toBeInstanceOf(OpenRPCDocumentDereferencingError); } @@ -282,26 +414,32 @@ describe("dereferenceDocument", () => { params: [], result: { name: "fooResult", - schema: true + schema: true, }, - links: [{ $ref: "#/components/links/fooLink" }] - } + links: [{ $ref: "#/components/links/fooLink" }], + }, ], components: { methods: { - notUsed: { name: "notUsed", params: [], result: { name: "unused", schema: true } } + notUsed: { + name: "notUsed", + params: [], + result: { name: "unused", schema: true }, + }, }, links: { fooLink: { - name: "fooLink" - } - } - } + name: "fooLink", + }, + }, + }, }; - const result = await dereferenceDocument(testDoc as OpenrpcDocument) as any; + const result = (await dereferenceDocument( + testDoc as OpenrpcDocument + )) as any; - expect(result.methods[0].links[0]).toBe(testDoc.components.links.fooLink) + expect(result.methods[0].links[0]).toBe(testDoc.components.links.fooLink); }); it("works with ref to a file", async () => { @@ -319,15 +457,17 @@ describe("dereferenceDocument", () => { params: [], result: { name: "fooResult", - schema: { $ref: `${__dirname}/good-schema.json` } - } - } - ] + schema: { $ref: `${__dirname}/good-schema.json` }, + }, + }, + ], }; - const result = await dereferenceDocument(testDoc as OpenrpcDocument) as any; + const result = (await dereferenceDocument( + testDoc as OpenrpcDocument + )) as any; - expect(result.methods[0].result.schema.type).toBe("string") + expect(result.methods[0].result.schema.type).toBe("string"); }); it("works with schema that makes ref to a schema from components", async () => { @@ -348,22 +488,26 @@ describe("dereferenceDocument", () => { schema: { type: "object", properties: { - foo: { $ref: "#/components/schemas/foo" } - } - } - } - } + foo: { $ref: "#/components/schemas/foo" }, + }, + }, + }, + }, ], components: { schemas: { - foo: { type: "string" } - } - } + foo: { type: "string" }, + }, + }, }; - const result = await dereferenceDocument(testDoc as OpenrpcDocument) as any; + const result = (await dereferenceDocument( + testDoc as OpenrpcDocument + )) as any; - expect(result.methods[0].result.schema.properties.foo).toBe(result.components.schemas.foo); + expect(result.methods[0].result.schema.properties.foo).toBe( + result.components.schemas.foo + ); }); it("throws when a schema cannot be resolved from components", async () => { @@ -384,22 +528,22 @@ describe("dereferenceDocument", () => { schema: { type: "object", properties: { - foo: { $ref: "#/components/schemas/foo" } - } - } - } - } + foo: { $ref: "#/components/schemas/foo" }, + }, + }, + }, + }, ], components: { schemas: { foo: { $ref: "#/components/schemas/bar" }, bar: { $ref: "#/not/real" }, - } - } + }, + }, }; try { - await dereferenceDocument(testDoc as OpenrpcDocument) as any; + (await dereferenceDocument(testDoc as OpenrpcDocument)) as any; } catch (e) { expect(e).toBeInstanceOf(OpenRPCDocumentDereferencingError); } diff --git a/src/dereference-document.ts b/src/dereference-document.ts index b4362466..521e932a 100644 --- a/src/dereference-document.ts +++ b/src/dereference-document.ts @@ -62,6 +62,7 @@ const handleSchemaWithSchemaComponents = async (s: JSONSchema, schemaComponents: const dereffed = await dereffer.resolve(); if (dereffed !== true && dereffed !== false) { delete dereffed.components; + delete s.components; } return dereffed; } catch (e: any) {