Skip to content

Commit 92dbaac

Browse files
authored
Merge pull request #6 from hey-api/fix/resolve-json
fix: correctly resolve json input
2 parents 6860977 + a92ca73 commit 92dbaac

File tree

254 files changed

+122
-13838
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

254 files changed

+122
-13838
lines changed

lib/__tests__/index.test.ts

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import path from 'node:path';
2+
3+
import { describe, expect, it } from 'vitest';
4+
5+
import { getResolvedInput } from '../index';
6+
7+
describe('getResolvedInput', () => {
8+
it('handles url', async () => {
9+
const pathOrUrlOrSchema = 'https://foo.com';
10+
const resolvedInput = await getResolvedInput({ pathOrUrlOrSchema });
11+
expect(resolvedInput.type).toBe('url');
12+
expect(resolvedInput.schema).toBeUndefined();
13+
expect(resolvedInput.path).toBe('https://foo.com/');
14+
});
15+
16+
it('handles file', async () => {
17+
const pathOrUrlOrSchema = './path/to/openapi.json';
18+
const resolvedInput = await getResolvedInput({ pathOrUrlOrSchema });
19+
expect(resolvedInput.type).toBe('file');
20+
expect(resolvedInput.schema).toBeUndefined();
21+
expect(resolvedInput.path).toBe(path.resolve('./path/to/openapi.json'));
22+
});
23+
24+
it('handles raw spec', async () => {
25+
const pathOrUrlOrSchema = {
26+
info: {
27+
version: '1.0.0',
28+
},
29+
openapi: '3.1.0',
30+
paths: {},
31+
};
32+
const resolvedInput = await getResolvedInput({ pathOrUrlOrSchema });
33+
expect(resolvedInput.type).toBe('json');
34+
expect(resolvedInput.schema).toEqual({
35+
info: {
36+
version: '1.0.0',
37+
},
38+
openapi: '3.1.0',
39+
paths: {},
40+
});
41+
expect(resolvedInput.path).toBe('');
42+
});
43+
});

lib/index.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export const getResolvedInput = ({
3838
// So we're being generous here and doing the conversion automatically.
3939
// This is not intended to be a 100% bulletproof solution.
4040
// If it doesn't work for your use-case, then use a URL instead.
41-
if (url.isFileSystemPath(resolvedInput.path)) {
41+
if (resolvedInput.path && url.isFileSystemPath(resolvedInput.path)) {
4242
resolvedInput.path = url.fromFileSystemPath(resolvedInput.path);
4343
resolvedInput.type = 'file';
4444
} else if (!resolvedInput.path && pathOrUrlOrSchema && typeof pathOrUrlOrSchema === 'object') {
@@ -54,8 +54,10 @@ export const getResolvedInput = ({
5454
}
5555
}
5656

57-
// resolve the absolute path of the schema
58-
resolvedInput.path = url.resolve(url.cwd(), resolvedInput.path);
57+
if (resolvedInput.type !== 'json') {
58+
// resolve the absolute path of the schema
59+
resolvedInput.path = url.resolve(url.cwd(), resolvedInput.path);
60+
}
5961

6062
return resolvedInput;
6163
}

lib/resolvers/url.ts

+49-49
Original file line numberDiff line numberDiff line change
@@ -14,73 +14,73 @@ export const sendRequest = async ({
1414
timeout?: number;
1515
url: URL | string;
1616
}): Promise<{
17+
init?: RequestInit;
1718
response: Response;
1819
}> => {
1920
url = new URL(url);
2021
redirects.push(url.href);
2122

22-
try {
23-
const controller = new AbortController();
24-
const timeoutId = setTimeout(() => {
25-
controller.abort();
26-
}, timeout);
27-
const response = await fetch(url, {
28-
signal: controller.signal,
29-
...init,
30-
});
31-
clearTimeout(timeoutId);
32-
33-
if (response.status >= 400) {
34-
// gracefully handle HEAD method not allowed
35-
if (response.status === 405 && init?.method === 'HEAD') {
36-
return { response };
37-
}
23+
const controller = new AbortController();
24+
const timeoutId = setTimeout(() => {
25+
controller.abort();
26+
}, timeout);
27+
const response = await fetch(url, {
28+
signal: controller.signal,
29+
...init,
30+
});
31+
clearTimeout(timeoutId);
3832

39-
throw ono({ status: response.status }, `HTTP ERROR ${response.status}`);
33+
if (response.status >= 300 && response.status <= 399) {
34+
if (redirects.length > 5) {
35+
throw new ResolverError(
36+
ono(
37+
{ status: response.status },
38+
`Error requesting ${redirects[0]}. \nToo many redirects: \n ${redirects.join(" \n ")}`,
39+
),
40+
);
4041
}
41-
42-
if (response.status >= 300) {
43-
if (redirects.length > 5) {
44-
throw new ResolverError(
45-
ono(
46-
{ status: response.status },
47-
`Error requesting ${redirects[0]}. \nToo many redirects: \n ${redirects.join(" \n ")}`,
48-
),
49-
);
50-
}
51-
52-
if (!("location" in response.headers) || !response.headers.location) {
53-
throw ono({ status: response.status }, `HTTP ${response.status} redirect with no location header`);
54-
}
55-
56-
return sendRequest({
57-
init,
58-
redirects,
59-
timeout,
60-
url: resolve(url.href, response.headers.location as string),
61-
});
42+
43+
if (!("location" in response.headers) || !response.headers.location) {
44+
throw ono({ status: response.status }, `HTTP ${response.status} redirect with no location header`);
6245
}
6346

64-
return { response };
65-
} catch (error: any) {
66-
throw new ResolverError(ono(error, `Error requesting ${url.href}`), url.href);
47+
return sendRequest({
48+
init,
49+
redirects,
50+
timeout,
51+
url: resolve(url.href, response.headers.location as string),
52+
});
6753
}
54+
55+
return { init, response };
6856
}
6957

7058
export const urlResolver = {
7159
handler: async (file: FileInfo, arrayBuffer?: ArrayBuffer): Promise<void> => {
7260
let data = arrayBuffer;
7361

7462
if (!data) {
75-
const { response } = await sendRequest({
76-
init: {
77-
method: 'GET',
78-
},
79-
url: file.url,
80-
});
81-
data = response.body ? await response.arrayBuffer() : new ArrayBuffer(0)
63+
try {
64+
const { init, response } = await sendRequest({
65+
init: {
66+
method: 'GET',
67+
},
68+
url: file.url,
69+
});
70+
71+
if (response.status >= 400) {
72+
// gracefully handle HEAD method not allowed
73+
if (response.status !== 405 || init?.method !== 'HEAD') {
74+
throw ono({ status: response.status }, `HTTP ERROR ${response.status}`);
75+
}
76+
77+
data = response.body ? await response.arrayBuffer() : new ArrayBuffer(0)
78+
}
79+
} catch (error: any) {
80+
throw new ResolverError(ono(error, `Error requesting ${file.url}`), file.url);
81+
}
8282
}
8383

84-
file.data = Buffer.from(data);
84+
file.data = Buffer.from(data!);
8585
},
8686
};

package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@hey-api/json-schema-ref-parser",
3-
"version": "1.0.2",
3+
"version": "1.0.3",
44
"description": "Parse, Resolve, and Dereference JSON Schema $ref pointers",
55
"homepage": "https://heyapi.dev/",
66
"repository": {
@@ -48,8 +48,8 @@
4848
"test:browser": "cross-env BROWSER=\"true\" yarn test",
4949
"test:node": "yarn test",
5050
"test:update": "vitest -u",
51-
"test:watch": "vitest -w",
52-
"test": "vitest --coverage",
51+
"test:watch": "vitest watch --config vitest.config.unit.ts",
52+
"test": "vitest run --config vitest.config.unit.ts",
5353
"typecheck": "tsc --noEmit"
5454
},
5555
"devDependencies": {

test/__IGNORED__/__({[ % & $ # @ ` ~ ,)}]__/__({[ % & $ # @ ` ~ ,)}]__.yaml

-5
This file was deleted.

test/__IGNORED__/__({[ % & $ # @ ` ~ ,)}]__/__({[ % & $ # @ ` ~ ,)}]__/__({[ % & $ # @ ` ~ ,)}]__.json

-12
This file was deleted.

test/__IGNORED__/__({[ % & $ # @ ` ~ ,)}]__/dereferenced.ts

-12
This file was deleted.

test/__IGNORED__/__({[ % & $ # @ ` ~ ,)}]__/parsed.ts

-25
This file was deleted.

test/__IGNORED__/__({[ % & $ # @ ` ~ ,)}]__/special-characters.spec.ts

-53
This file was deleted.

test/fixtures/server.ts

-68
This file was deleted.

0 commit comments

Comments
 (0)