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

feat: adds support for Data API in Github#getFileContents #551

Merged
merged 2 commits into from
Sep 11, 2020
Merged
Show file tree
Hide file tree
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
6 changes: 6 additions & 0 deletions __snapshots__/github.js

Large diffs are not rendered by default.

77 changes: 64 additions & 13 deletions src/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -692,7 +692,6 @@ export class GitHub {
updates: Update[],
defaultBranch: string
): Promise<Changes> {
const refName = `refs/heads/${defaultBranch}`;
const changes = new Map();
for (const update of updates) {
let content;
Expand All @@ -702,17 +701,11 @@ export class GitHub {
// hit GitHub again.
content = {data: update.contents};
} else {
content = await this.request(
`GET /repos/:owner/:repo/contents/:path${
this.proxyKey ? `?key=${this.proxyKey}` : ''
}`,
{
owner: this.owner,
repo: this.repo,
path: update.path,
ref: refName,
}
const fileContent = await this.getFileContents(
update.path,
defaultBranch
);
content = {data: fileContent};
}
} catch (err) {
if (err.status !== 404) throw err;
Expand Down Expand Up @@ -769,24 +762,82 @@ export class GitHub {
);
}

async getFileContents(path: string): Promise<GitHubFileContents> {
async getFileContentsWithSimpleAPI(
path: string,
defaultBranch: string | undefined
): Promise<GitHubFileContents> {
const options: any = {
owner: this.owner,
repo: this.repo,
path,
};
if (defaultBranch) {
options.ref = `refs/heads/${defaultBranch}`;
}
const resp = await this.request(
`GET /repos/:owner/:repo/contents/:path${
this.proxyKey ? `?key=${this.proxyKey}` : ''
}`,
options
);
return {
parsedContent: Buffer.from(resp.data.content, 'base64').toString('utf8'),
content: resp.data.content,
sha: resp.data.sha,
};
}

async getFileContentsWithDataAPI(
path: string,
defaultBranch: string | undefined
): Promise<GitHubFileContents> {
const repoTree = await this.request(
`GET /repos/:owner/:repo/git/trees/:branch${
this.proxyKey ? `?key=${this.proxyKey}` : ''
}`,
{
owner: this.owner,
repo: this.repo,
path,
branch: defaultBranch,
}
);

const blobDescriptor = repoTree.data.tree.find(
(tree: any) => tree.path === path
);

const resp = await this.request(
`GET /repos/:owner/:repo/git/blobs/:sha${
this.proxyKey ? `?key=${this.proxyKey}` : ''
}`,
{
owner: this.owner,
repo: this.repo,
sha: blobDescriptor.sha,
}
);

return {
parsedContent: Buffer.from(resp.data.content, 'base64').toString('utf8'),
content: resp.data.content,
sha: resp.data.sha,
};
}

async getFileContents(
path: string,
defaultBranch: string | undefined = undefined
): Promise<GitHubFileContents> {
try {
return await this.getFileContentsWithSimpleAPI(path, defaultBranch);
} catch (err) {
if (err.status === 403) {
return await this.getFileContentsWithDataAPI(path, defaultBranch);
}
throw err;
}
}

async createRelease(
packageName: string,
version: string,
Expand Down
11 changes: 11 additions & 0 deletions test/fixtures/github-data-api/403-too-large-file-response.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"message": "This API returns blobs up to 1 MB in size. The requested blob is too large to fetch via the API, but you can use the Git Data API to request blobs up to 100 MB in size.",
"errors": [
{
"resource": "Blob",
"field": "data",
"code": "too_large"
}
],
"documentation_url": "https://docs.github.com/rest/reference/repos#get-repository-content"
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"sha": "1630d12c8e72e7f1441ae8afe2240d823c2d4804",
"url": "https://api.github.com/repos/lancedikson/release-please-403-example/git/trees/1630d12c8e72e7f1441ae8afe2240d823c2d4804",
"tree": [
{
"path": ".github",
"mode": "040000",
"type": "tree",
"sha": "cc64165cf5da91810ab7edc1143a47be42513c0a",
"url": "https://api.github.com/repos/lancedikson/release-please-403-example/git/trees/cc64165cf5da91810ab7edc1143a47be42513c0a"
},
{
"path": ".gitignore",
"mode": "100644",
"type": "blob",
"sha": "3c3629e647f5ddf82548912e337bea9826b434af",
"size": 13,
"url": "https://api.github.com/repos/lancedikson/release-please-403-example/git/blobs/3c3629e647f5ddf82548912e337bea9826b434af"
},
{
"path": "package-lock.json",
"mode": "100644",
"type": "blob",
"sha": "2f3d2c47bf49f81aca0df9ffc49524a213a2dc33",
"size": 1352120,
"url": "https://api.github.com/repos/lancedikson/release-please-403-example/git/blobs/2f3d2c47bf49f81aca0df9ffc49524a213a2dc33"
},
{
"path": "package.json",
"mode": "100644",
"type": "blob",
"sha": "33f90c139fca3d99a08a934fb87d30c13c68b885",
"size": 3995,
"url": "https://api.github.com/repos/lancedikson/release-please-403-example/git/blobs/33f90c139fca3d99a08a934fb87d30c13c68b885"
}
],
"truncated": false
}
62 changes: 62 additions & 0 deletions test/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,4 +201,66 @@ describe('GitHub', () => {
req.done();
});
});

describe('getFileContents', () => {
it('should support Github Data API in case of a big file', async () => {
const simpleAPIResponse = JSON.parse(
readFileSync(
resolve(
fixturesPath,
'github-data-api',
'403-too-large-file-response.json'
),
'utf8'
)
);
const dataAPITreesResponse = JSON.parse(
readFileSync(
resolve(
fixturesPath,
'github-data-api',
'data-api-trees-successful-response.json'
),
'utf8'
)
);
const dataAPIBlobResponse = JSON.parse(
readFileSync(
resolve(
fixturesPath,
'github-data-api',
'data-api-blobs-successful-response.json'
),
'utf8'
)
);
const req1 = nock('https://api.github.com')
.get(
'/repos/fake/fake/contents/package-lock.json?ref=refs%2Fheads%2Fmaster'
)
.reply(403, simpleAPIResponse);
const req2 = nock('https://api.github.com')
.get('/repos/fake/fake/git/trees/master')
.reply(200, dataAPITreesResponse);
const req3 = nock('https://api.github.com')
.get(
'/repos/fake/fake/git/blobs/2f3d2c47bf49f81aca0df9ffc49524a213a2dc33'
)
.reply(200, dataAPIBlobResponse);

const fileContents = await github.getFileContents(
'package-lock.json',
'master'
);
expect(fileContents).to.have.property('content');
expect(fileContents).to.have.property('parsedContent');
expect(fileContents)
.to.have.property('sha')
.equal('2f3d2c47bf49f81aca0df9ffc49524a213a2dc33');
snapshot(fileContents);
req1.done();
req2.done();
req3.done();
});
});
});