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(presets): local bitbucket-server presets #7000

Merged
merged 5 commits into from
Aug 19, 2020
Merged
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`config/presets/bitbucket-server/index fetchJSONFile() returns JSON 1`] = `
Object {
"from": "api",
}
`;

exports[`config/presets/bitbucket-server/index fetchJSONFile() returns JSON 2`] = `
Array [
Object {
"headers": Object {
"accept": "application/json",
"accept-encoding": "gzip, deflate",
"authorization": "Bearer abc",
"host": "git.company.org",
"user-agent": "https://github.com/renovatebot/renovate",
"x-atlassian-token": "no-check",
},
"method": "GET",
"url": "https://git.company.org/rest/api/1.0/projects/some/repos/repo/browse/some-filename.json?limit=20000",
},
]
`;

exports[`config/presets/bitbucket-server/index fetchJSONFile() throws 404 1`] = `
Array [
Object {
"headers": Object {
"accept": "application/json",
"accept-encoding": "gzip, deflate",
"authorization": "Bearer abc",
"host": "git.company.org",
"user-agent": "https://github.com/renovatebot/renovate",
"x-atlassian-token": "no-check",
},
"method": "GET",
"url": "https://git.company.org/rest/api/1.0/projects/some/repos/repo/browse/some-filename.json?limit=20000",
},
]
`;

exports[`config/presets/bitbucket-server/index fetchJSONFile() throws to big 1`] = `
Array [
Object {
"headers": Object {
"accept": "application/json",
"accept-encoding": "gzip, deflate",
"authorization": "Bearer abc",
"host": "git.company.org",
"user-agent": "https://github.com/renovatebot/renovate",
"x-atlassian-token": "no-check",
},
"method": "GET",
"url": "https://git.company.org/rest/api/1.0/projects/some/repos/repo/browse/some-filename.json?limit=20000",
},
]
`;

exports[`config/presets/bitbucket-server/index fetchJSONFile() throws to invalid 1`] = `
Array [
Object {
"headers": Object {
"accept": "application/json",
"accept-encoding": "gzip, deflate",
"authorization": "Bearer abc",
"host": "git.company.org",
"user-agent": "https://github.com/renovatebot/renovate",
"x-atlassian-token": "no-check",
},
"method": "GET",
"url": "https://git.company.org/rest/api/1.0/projects/some/repos/repo/browse/some-filename.json?limit=20000",
},
]
`;

exports[`config/presets/bitbucket-server/index getPresetFromEndpoint() uses custom endpoint 1`] = `
Array [
Object {
"headers": Object {
"accept": "application/json",
"accept-encoding": "gzip, deflate",
"authorization": "Bearer abc",
"host": "api.github.example.org",
"user-agent": "https://github.com/renovatebot/renovate",
"x-atlassian-token": "no-check",
},
"method": "GET",
"url": "https://api.github.example.org/rest/api/1.0/projects/some/repos/repo/browse/default.json?limit=20000",
},
]
`;
123 changes: 123 additions & 0 deletions lib/config/presets/bitbucket-server/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import * as httpMock from '../../../../test/httpMock';
import { getName, mocked } from '../../../../test/util';
import * as _hostRules from '../../../util/host-rules';
import { PRESET_DEP_NOT_FOUND } from '../util';
import * as bitbucketServer from '.';

jest.mock('../../../util/host-rules');

const hostRules = mocked(_hostRules);

const bitbucketApiHost = 'https://git.company.org';
const basePath = '/rest/api/1.0/projects/some/repos/repo/browse';

describe(getName(__filename), () => {
beforeEach(() => {
httpMock.setup();
hostRules.find.mockReturnValue({ token: 'abc' });
});

afterEach(() => httpMock.reset());

describe('fetchJSONFile()', () => {
it('returns JSON', async () => {
httpMock
.scope(bitbucketApiHost)
.get(`${basePath}/some-filename.json`)
.query({ limit: 20000 })
.reply(200, {
isLastPage: true,
lines: ['{"from":"api"}'],
});

const res = await bitbucketServer.fetchJSONFile(
'some/repo',
'some-filename.json',
bitbucketApiHost
);
expect(res).toMatchSnapshot();
expect(httpMock.getTrace()).toMatchSnapshot();
});

it('throws 404', async () => {
httpMock
.scope(bitbucketApiHost)
.get(`${basePath}/some-filename.json`)
.query({ limit: 20000 })
.reply(404);

await expect(
bitbucketServer.fetchJSONFile(
'some/repo',
'some-filename.json',
bitbucketApiHost
)
).rejects.toThrow(PRESET_DEP_NOT_FOUND);
expect(httpMock.getTrace()).toMatchSnapshot();
});

it('throws to big', async () => {
httpMock
.scope(bitbucketApiHost)
.get(`${basePath}/some-filename.json`)
.query({ limit: 20000 })
.reply(200, {
isLastPage: false,
size: 50000,
lines: ['{"from":"api"}'],
});

await expect(
bitbucketServer.fetchJSONFile(
'some/repo',
'some-filename.json',
bitbucketApiHost
)
).rejects.toThrow('invalid preset JSON');
expect(httpMock.getTrace()).toMatchSnapshot();
});

it('throws to invalid', async () => {
httpMock
.scope(bitbucketApiHost)
.get(`${basePath}/some-filename.json`)
.query({ limit: 20000 })
.reply(200, {
isLastPage: true,
lines: ['{"from":"api"'],
});

await expect(
bitbucketServer.fetchJSONFile(
'some/repo',
'some-filename.json',
bitbucketApiHost
)
).rejects.toThrow('invalid preset JSON');
expect(httpMock.getTrace()).toMatchSnapshot();
});
});

describe('getPresetFromEndpoint()', () => {
it('uses custom endpoint', async () => {
httpMock
.scope('https://api.github.example.org')
.get(`${basePath}/default.json`)
.query({ limit: 20000 })
.reply(200, {
isLastPage: true,
lines: ['{"from":"api"}'],
});
expect(
await bitbucketServer
.getPresetFromEndpoint(
'some/repo',
'default',
'https://api.github.example.org'
)
.catch(() => ({ from: 'api' }))
).toEqual({ from: 'api' });
expect(httpMock.getTrace()).toMatchSnapshot();
});
});
});
60 changes: 60 additions & 0 deletions lib/config/presets/bitbucket-server/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { logger } from '../../../logger';
import { ExternalHostError } from '../../../types/errors/external-host-error';
import { FileData } from '../../../types/platform/bitbucket-server';
import {
BitbucketServerHttp,
setBaseUrl,
} from '../../../util/http/bitbucket-server';
import { Preset } from '../common';
import { PRESET_DEP_NOT_FOUND, fetchPreset } from '../util';

const http = new BitbucketServerHttp();

export async function fetchJSONFile(
repo: string,
fileName: string,
endpoint: string
): Promise<Preset> {
const [projectKey, repositorySlug] = repo.split('/');
setBaseUrl(endpoint);
const url = `/rest/api/1.0/projects/${projectKey}/repos/${repositorySlug}/browse/${fileName}?limit=20000`;
let res: { body: FileData };
try {
res = await http.getJson(url);
} catch (err) {
console.error({ url, err });
// istanbul ignore if: not testable with nock
if (err instanceof ExternalHostError) {
throw err;
}
logger.debug(
{ statusCode: err.statusCode },
`Failed to retrieve ${fileName} from repo`
);
throw new Error(PRESET_DEP_NOT_FOUND);
}
if (!res.body.isLastPage) {
logger.warn({ size: res.body.size }, 'Renovate config to big');
throw new Error('invalid preset JSON');
}
try {
const content = res.body.lines.join();
const parsed = JSON.parse(content);
return parsed;
} catch (err) {
throw new Error('invalid preset JSON');
}
}

export function getPresetFromEndpoint(
pkgName: string,
filePreset: string,
endpoint: string
): Promise<Preset> {
return fetchPreset({
pkgName,
filePreset,
endpoint,
fetch: fetchJSONFile,
});
}
12 changes: 12 additions & 0 deletions lib/config/presets/local/__snapshots__/index.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`config/presets/local/index getPreset() forwards to custom bitbucket-server 1`] = `
Array [
Array [
"some/repo",
"default",
"https://git.example.com",
],
]
`;

exports[`config/presets/local/index getPreset() forwards to custom bitbucket-server 2`] = `undefined`;

exports[`config/presets/local/index getPreset() forwards to custom github 1`] = `
Array [
Array [
Expand Down
18 changes: 18 additions & 0 deletions lib/config/presets/local/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { getName, mocked } from '../../../../test/util';
import * as _bitbucketServer from '../bitbucket-server';
import * as _github from '../github';
import * as _gitlab from '../gitlab';
import * as local from '.';

jest.mock('../gitlab');
jest.mock('../github');
jest.mock('../bitbucket-server');

const gitlab = mocked(_gitlab);
const github = mocked(_github);
const bitbucketServer = mocked(_bitbucketServer);

describe(getName(__filename), () => {
beforeEach(() => {
Expand Down Expand Up @@ -84,5 +87,20 @@ describe(getName(__filename), () => {
expect(github.getPresetFromEndpoint.mock.calls).toMatchSnapshot();
expect(content).toMatchSnapshot();
});

it('forwards to custom bitbucket-server', async () => {
const content = await local.getPreset({
packageName: 'some/repo',
presetName: 'default',
baseConfig: {
platform: 'bitbucket-server',
endpoint: 'https://git.example.com',
},
});
expect(
bitbucketServer.getPresetFromEndpoint.mock.calls
).toMatchSnapshot();
expect(content).toMatchSnapshot();
});
});
});
8 changes: 8 additions & 0 deletions lib/config/presets/local/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import {
PLATFORM_TYPE_BITBUCKET_SERVER,
PLATFORM_TYPE_GITHUB,
PLATFORM_TYPE_GITLAB,
} from '../../../constants/platforms';
import * as bitbucketServer from '../bitbucket-server';
import { Preset, PresetConfig } from '../common';
import * as github from '../github';
import * as gitlab from '../gitlab';
Expand All @@ -20,6 +22,12 @@ export function getPreset({
return gitlab.getPresetFromEndpoint(pkgName, presetName, endpoint);
case PLATFORM_TYPE_GITHUB:
return github.getPresetFromEndpoint(pkgName, presetName, endpoint);
case PLATFORM_TYPE_BITBUCKET_SERVER:
return bitbucketServer.getPresetFromEndpoint(
pkgName,
presetName,
endpoint
);
default:
throw new Error(
`Unsupported platform '${baseConfig.platform}' for local preset.`
Expand Down
10 changes: 10 additions & 0 deletions lib/types/platform/bitbucket-server/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* https://docs.atlassian.com/bitbucket-server/rest/5.16.0/bitbucket-rest.html#idm8297065392
*/
export interface FileData {
isLastPage: boolean;

lines: string[];

size: number;
}