From dd85381c2b9e08ba9bca4d5beeee351f921c6043 Mon Sep 17 00:00:00 2001 From: Zachary Belford Date: Wed, 28 Oct 2020 14:58:05 -0700 Subject: [PATCH 1/2] feat: handle hash fragment of json pointers --- src/index.test.ts | 34 ++++++++++++++++++++++++++++++++++ src/reference-resolver.ts | 39 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/src/index.test.ts b/src/index.test.ts index 81af33c..b6b0d54 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -87,3 +87,37 @@ describe("referenceResolver", () => { } }); }); + + +describe("refs with hash fragment / internal reference component", () => { + it("works with file paths", async () => { + expect(await referenceResolver("./src/test-obj.json#/type", {})).toBe("string"); + }); + + describe("urls", () => { + it("works with forward slashes surrounding the hash", async () => { + expect(await referenceResolver("https://meta.open-rpc.org/#/type", {})).toBe("object"); + }); + it("works without slash infront of hash, but with one after", async () => { + expect(await referenceResolver("https://meta.open-rpc.org#/type", {})).toBe("object"); + }); + + it("errors when the json pointer is invalid", async () => { + expect.assertions(1); + try { + await referenceResolver("https://meta.open-rpc.org/#type", {}); + } catch (e) { + expect(e).toBeInstanceOf(InvalidRemoteURLError); + } + }); + + it("errors when you have 2 hash fragments in 1 ref", async () => { + expect.assertions(1); + try { + await referenceResolver("https://meta.open-rpc.org/#properties/#openrpc", {}); + } catch (e) { + expect(e).toBeInstanceOf(InvalidRemoteURLError); + } + }); + }); +}); diff --git a/src/reference-resolver.ts b/src/reference-resolver.ts index b3d9f53..708d164 100644 --- a/src/reference-resolver.ts +++ b/src/reference-resolver.ts @@ -136,8 +136,21 @@ export default (fetch: any, fs: any) => { } } - if (await fileExistsAndReadable(ref) === true) { - const fileContents = await readFile(ref); + // copy hash fragment from the filepath / url + const hashFragmentSplit = ref.split("#"); + let hashFragment; + if (hashFragmentSplit.length > 1) { + hashFragment = hashFragmentSplit[hashFragmentSplit.length - 1]; + } + + let hashlessRef = ref; + if (hashFragment) { + hashlessRef = ref.replace(`#${hashFragment}`, ""); + } + + if (await fileExistsAndReadable(hashlessRef) === true) { + // pull off the hash fragment first + const fileContents = await readFile(hashlessRef); let reffedSchema; try { reffedSchema = JSON.parse(fileContents); @@ -145,13 +158,33 @@ export default (fetch: any, fs: any) => { throw new NonJsonRefError({ $ref: ref }, fileContents); } + if (hashFragment) { + try { + const pointer = Ptr.parse(hashFragment); + return Promise.resolve(pointer.eval(reffedSchema)); + } catch (e) { + throw new InvalidJsonPointerRefError({ $ref: ref }); + } + } + return reffedSchema; } else if (isUrlLike(ref) === false) { throw new InvalidFileSystemPathError(ref); } try { - return await fetch(ref).then((r: any) => r.json()); + // leave the hash fragment on + // but evaluate the result after + const result = await fetch(ref).then((r: any) => r.json()); + if (hashFragment) { + try { + const pointer = Ptr.parse(hashFragment); + return Promise.resolve(pointer.eval(result)); + } catch (e) { + throw new InvalidJsonPointerRefError({ $ref: ref }); + } + } + return result; } catch (e) { throw new InvalidRemoteURLError(ref); } From 84da5c89d282cd99972d745ce52b19517882ccb9 Mon Sep 17 00:00:00 2001 From: Zachary Belford Date: Wed, 28 Oct 2020 15:09:09 -0700 Subject: [PATCH 2/2] fix: make test coverage 100 and fix test case --- src/index.test.ts | 19 +++++++++++++++---- src/reference-resolver.ts | 27 +++++++++++++-------------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/index.test.ts b/src/index.test.ts index b6b0d54..fac48ce 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -90,8 +90,19 @@ describe("referenceResolver", () => { describe("refs with hash fragment / internal reference component", () => { - it("works with file paths", async () => { - expect(await referenceResolver("./src/test-obj.json#/type", {})).toBe("string"); + describe("files", () => { + it("works in simple case", async () => { + expect(await referenceResolver("./src/test-obj.json#/type", {})).toBe("string"); + }); + + it("errors when the json pointer is invalid", async () => { + expect.assertions(1); + try { + await referenceResolver("./src/test-obj.json#balony", {}); + } catch (e) { + expect(e).toBeInstanceOf(InvalidJsonPointerRefError); + } + }); }); describe("urls", () => { @@ -107,7 +118,7 @@ describe("refs with hash fragment / internal reference component", () => { try { await referenceResolver("https://meta.open-rpc.org/#type", {}); } catch (e) { - expect(e).toBeInstanceOf(InvalidRemoteURLError); + expect(e).toBeInstanceOf(InvalidJsonPointerRefError); } }); @@ -116,7 +127,7 @@ describe("refs with hash fragment / internal reference component", () => { try { await referenceResolver("https://meta.open-rpc.org/#properties/#openrpc", {}); } catch (e) { - expect(e).toBeInstanceOf(InvalidRemoteURLError); + expect(e).toBeInstanceOf(InvalidJsonPointerRefError); } }); }); diff --git a/src/reference-resolver.ts b/src/reference-resolver.ts index 708d164..8d2b915 100644 --- a/src/reference-resolver.ts +++ b/src/reference-resolver.ts @@ -136,7 +136,6 @@ export default (fetch: any, fs: any) => { } } - // copy hash fragment from the filepath / url const hashFragmentSplit = ref.split("#"); let hashFragment; if (hashFragmentSplit.length > 1) { @@ -149,7 +148,6 @@ export default (fetch: any, fs: any) => { } if (await fileExistsAndReadable(hashlessRef) === true) { - // pull off the hash fragment first const fileContents = await readFile(hashlessRef); let reffedSchema; try { @@ -172,22 +170,23 @@ export default (fetch: any, fs: any) => { throw new InvalidFileSystemPathError(ref); } + let result; try { - // leave the hash fragment on - // but evaluate the result after - const result = await fetch(ref).then((r: any) => r.json()); - if (hashFragment) { - try { - const pointer = Ptr.parse(hashFragment); - return Promise.resolve(pointer.eval(result)); - } catch (e) { - throw new InvalidJsonPointerRefError({ $ref: ref }); - } - } - return result; + result = await fetch(ref).then((r: any) => r.json()); } catch (e) { throw new InvalidRemoteURLError(ref); } + + if (hashFragment) { + try { + const pointer = Ptr.parse(hashFragment); + return Promise.resolve(pointer.eval(result)); + } catch (e) { + throw new InvalidJsonPointerRefError({ $ref: ref }); + } + } + + return result; }; return resolveReference;