Skip to content

Commit

Permalink
chore(http/github): add utility function to fetch raw files (#30155)
Browse files Browse the repository at this point in the history
Co-authored-by: Rhys Arkins <rhys@arkins.net>
  • Loading branch information
secustor and rarkins committed Aug 21, 2024
1 parent 302718f commit 1f3ab66
Show file tree
Hide file tree
Showing 2 changed files with 200 additions and 0 deletions.
163 changes: 163 additions & 0 deletions lib/util/http/github.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Buffer } from 'node:buffer';
import { codeBlock } from 'common-tags';
import { DateTime } from 'luxon';
import * as httpMock from '../../../test/http-mock';
Expand Down Expand Up @@ -786,4 +787,166 @@ describe('util/http/github', () => {
).rejects.toThrow(EXTERNAL_HOST_ERROR);
});
});

describe('getRawFile()', () => {
it('add header and return', async () => {
httpMock
.scope(githubApiHost)
.get('/foo/bar/contents/lore/ipsum.txt')
.matchHeader(
'accept',
'application/vnd.github.raw+json, application/vnd.github.v3+json',
)
.reply(200, 'foo');
await expect(
githubApi.getRawTextFile(
`${githubApiHost}/foo/bar/contents/lore/ipsum.txt`,
),
).resolves.toMatchObject({
body: 'foo',
});
});

it('support relative path', async () => {
httpMock
.scope(githubApiHost)
.get('/foo/bar/contents/lore/ipsum.txt')
.matchHeader(
'accept',
'application/vnd.github.raw+json, application/vnd.github.v3+json',
)
.reply(200, 'foo');
await expect(
githubApi.getRawTextFile(
`${githubApiHost}/foo/bar/contents/foo/../lore/ipsum.txt`,
),
).resolves.toMatchObject({
body: 'foo',
});
});

it('support default to api.github.com if no baseURL has been supplied', async () => {
httpMock
.scope(githubApiHost)
.get('/foo/bar/contents/lore/ipsum.txt')
.matchHeader(
'accept',
'application/vnd.github.raw+json, application/vnd.github.v3+json',
)
.reply(200, 'foo');
await expect(
githubApi.getRawTextFile(`foo/bar/contents/lore/ipsum.txt`),
).resolves.toMatchObject({
body: 'foo',
});
});

it('support custom host if a baseURL has been supplied', async () => {
const customApiHost = 'https://my.comapny.com/api/v3/';
httpMock
.scope(customApiHost)
.get('/foo/bar/contents/lore/ipsum.txt')
.matchHeader(
'accept',
'application/vnd.github.raw+json, application/vnd.github.v3+json',
)
.reply(200, 'foo');
await expect(
githubApi.getRawTextFile(`foo/bar/contents/lore/ipsum.txt`, {
baseUrl: customApiHost,
}),
).resolves.toMatchObject({
body: 'foo',
});
});

it('support default to api.github.com if no baseURL, but repository has been supplied', async () => {
httpMock
.scope(githubApiHost)
.get('/foo/bar/contents/lore/ipsum.txt')
.matchHeader(
'accept',
'application/vnd.github.raw+json, application/vnd.github.v3+json',
)
.reply(200, 'foo');
await expect(
githubApi.getRawTextFile(`lore/ipsum.txt`, {
repository: 'foo/bar',
}),
).resolves.toMatchObject({
body: 'foo',
});
});

it('support custom host if a baseURL and repository has been supplied', async () => {
const customApiHost = 'https://my.comapny.com/api/v3/';
httpMock
.scope(customApiHost)
.get('/foo/bar/contents/lore/ipsum.txt')
.matchHeader(
'accept',
'application/vnd.github.raw+json, application/vnd.github.v3+json',
)
.reply(200, 'foo');
await expect(
githubApi.getRawTextFile(`lore/ipsum.txt`, {
baseUrl: customApiHost,
repository: 'foo/bar',
}),
).resolves.toMatchObject({
body: 'foo',
});
});

it('support default to api.github.com if content path is used', async () => {
httpMock
.scope(githubApiHost)
.get('/foo/bar/contents/lore/ipsum.txt')
.matchHeader(
'accept',
'application/vnd.github.raw+json, application/vnd.github.v3+json',
)
.reply(200, 'foo');
await expect(
githubApi.getRawTextFile(`foo/bar/contents/lore/ipsum.txt`),
).resolves.toMatchObject({
body: 'foo',
});
});

it('support custom host if content path is used', async () => {
const customApiHost = 'https://my.comapny.com/api/v3/';
httpMock
.scope(customApiHost)
.get('/foo/bar/contents/lore/ipsum.txt')
.matchHeader(
'accept',
'application/vnd.github.raw+json, application/vnd.github.v3+json',
)
.reply(200, 'test');
await expect(
githubApi.getRawTextFile(`foo/bar/contents/lore/ipsum.txt`, {
baseUrl: customApiHost,
}),
).resolves.toMatchObject({
body: 'test',
});
});

it('throw error if a ', async () => {
httpMock
.scope(githubApiHost)
.get('/foo/bar/contents/lore/ipsum.bin')
.matchHeader(
'accept',
'application/vnd.github.raw+json, application/vnd.github.v3+json',
)
.reply(200, Buffer.from('foo', 'binary'));
await expect(
githubApi.getRawTextFile(`foo/bar/contents/lore/ipsum.bin`, {
responseType: 'buffer',
}),
).rejects.toThrow();
});
});
});
37 changes: 37 additions & 0 deletions lib/util/http/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -493,4 +493,41 @@ export class GithubHttp extends Http<GithubHttpOptions> {

return result;
}

/**
* Get the raw text file from a URL.
* Only use this method to fetch text files.
*
* @param url Full API URL, contents path or path inside the repository to the file
* @param options
*
* @example url = 'https://api.github.com/repos/renovatebot/renovate/contents/package.json'
* @example url = 'renovatebot/renovate/contents/package.json'
* @example url = 'package.json' & options.repository = 'renovatebot/renovate'
*/
public async getRawTextFile(
url: string,
options: InternalHttpOptions & GithubHttpOptions = {},
): Promise<HttpResponse> {
const newOptions: InternalHttpOptions & GithubHttpOptions = {
...options,
headers: {
accept: 'application/vnd.github.raw+json',
},
};

let newURL = url;
const httpRegex = regEx(/^https?:\/\//);
if (options.repository && !httpRegex.test(options.repository)) {
newURL = joinUrlParts(options.repository, 'contents', url);
}

const result = await this.get(newURL, newOptions);
if (!is.string(result.body)) {
throw new Error(
`Expected raw text file but received ${typeof result.body}`,
);
}
return result;
}
}

0 comments on commit 1f3ab66

Please sign in to comment.