Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(oas-normalize): support for basic auth in url strings #928

Merged
merged 2 commits into from
Jan 25, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/oas-normalize/src/index.ts
Original file line number Diff line number Diff line change
@@ -63,7 +63,8 @@ export default class OASNormalize {
return resolve(this.file.toString());

case 'url':
const resp = await fetch(utils.normalizeURL(this.file)).then(res => res.text());
const { url, options } = utils.prepareURL(this.file);
const resp = await fetch(url, options).then(res => res.text());
return resolve(resp);

case 'path':
31 changes: 26 additions & 5 deletions packages/oas-normalize/src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -14,13 +14,34 @@ export function isBuffer(obj: any) {
}

/**
* Converts GitHub blob URLs to raw URLs
* Deconstruct a URL into a payload for a `fetch` request.
*
*/
export function normalizeURL(url: string) {
if (url.startsWith('https://github.com/') && url.includes('/blob/')) {
return url.replace('github.com', 'raw.githubusercontent.com').replace('/blob/', '/');
export function prepareURL(url: string) {
const options: RequestInit = {};
const u = new URL(url);

// `fetch` doesn't support supplying basic auth credentials in the URL so we need to move them
// into a header.
if (u.username || u.password) {
options.headers = {
Authorization: `Basic ${btoa(`${u.username}:${u.password}`)}`,
};

u.username = '';
u.password = '';
}
return url;

// Transform GitHub sources into their raw content URLs.
if (u.host === 'github.com' && u.pathname.includes('/blob/')) {
u.host = 'raw.githubusercontent.com';
u.pathname = u.pathname.replace('/blob/', '/');
}

return {
url: u.toString(),
options,
};
}

/**
18 changes: 16 additions & 2 deletions packages/oas-normalize/test/index.test.ts
Original file line number Diff line number Diff line change
@@ -16,8 +16,8 @@ describe('#load', () => {
['OpenAPI 3.0', '3.0'],
['OpenAPI 3.1', '3.1'],
])('%s support', (_, version) => {
let json;
let yaml;
let json: Record<string, unknown>;
let yaml: string;

beforeEach(async () => {
json = await import(`@readme/oas-examples/${version}/json/petstore.json`).then(r => r.default);
@@ -67,6 +67,20 @@ describe('#load', () => {
await expect(o.load()).resolves.toStrictEqual(json);
});

it('should support URLs with basic auth', async () => {
nock('https://@example.com', {
reqheaders: {
Authorization: `Basic ${btoa('username:password')}`,
},
})
.get(`/api-${version}.json`)
.reply(200, json);

const o = new OASNormalize(`https://username:[email protected]/api-${version}.json`);

await expect(o.load()).resolves.toStrictEqual(json);
});

it('should convert GitHub repo URLs to raw URLs', async () => {
nock('https://raw.githubusercontent.com')
.get('/readmeio/oas-examples/main/3.0/json/petstore.json')