backend-common: refactor GitlabUrlReader test and restore raw url functionality
This commit is contained in:
@@ -14,139 +14,109 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { GitlabUrlReader, readConfig } from './GitlabUrlReader';
|
||||
import { rest } from 'msw';
|
||||
import { setupServer } from 'msw/node';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { getVoidLogger } from '../logging';
|
||||
import { GitlabUrlReader } from './GitlabUrlReader';
|
||||
|
||||
const logger = getVoidLogger();
|
||||
|
||||
describe('GitlabUrlReader', () => {
|
||||
const createConfig = (token: string | undefined) =>
|
||||
ConfigReader.fromConfigs([
|
||||
{
|
||||
context: '',
|
||||
data: {
|
||||
integrations: {
|
||||
gitlab: [
|
||||
{
|
||||
host: 'gitlab.com',
|
||||
token: token,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
const worker = setupServer();
|
||||
|
||||
it('should build project urls', () => {
|
||||
const processor = new GitlabUrlReader(
|
||||
readConfig(createConfig(undefined))[0],
|
||||
beforeAll(() => worker.listen({ onUnhandledRequest: 'error' }));
|
||||
afterAll(() => worker.close());
|
||||
|
||||
beforeEach(() => {
|
||||
worker.use(
|
||||
rest.get('*/api/v4/projects/:name', (_, res, ctx) =>
|
||||
res(ctx.status(200), ctx.json({ id: 12345 })),
|
||||
),
|
||||
rest.get('*', (req, res, ctx) =>
|
||||
res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
url: req.url.toString(),
|
||||
headers: req.headers.getAllHeaders(),
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
afterEach(() => worker.resetHandlers());
|
||||
|
||||
const createConfig = (token?: string) =>
|
||||
new ConfigReader(
|
||||
{
|
||||
integrations: { gitlab: [{ host: 'gitlab.com', token }] },
|
||||
},
|
||||
'test-config',
|
||||
);
|
||||
|
||||
const tests = [
|
||||
{
|
||||
target:
|
||||
'https://gitlab.com/groupA/teams/teamA/subgroupA/repoA/-/blob/branch/my/path/to/file.yaml',
|
||||
url: new URL(
|
||||
it.each([
|
||||
// Project URLs
|
||||
{
|
||||
url:
|
||||
'https://gitlab.com/groupA/teams/teamA/subgroupA/repoA/-/blob/branch/my/path/to/file.yaml',
|
||||
config: createConfig(),
|
||||
response: expect.objectContaining({
|
||||
url:
|
||||
'https://gitlab.com/api/v4/projects/12345/repository/files/my%2Fpath%2Fto%2Ffile.yaml/raw?ref=branch',
|
||||
),
|
||||
err: undefined,
|
||||
},
|
||||
{
|
||||
target:
|
||||
'https://gitlab.example.com/groupA/teams/teamA/subgroupA/repoA/-/blob/branch/my/path/to/file.yaml',
|
||||
url: new URL(
|
||||
headers: expect.objectContaining({
|
||||
'private-token': '',
|
||||
}),
|
||||
}),
|
||||
},
|
||||
{
|
||||
url:
|
||||
'https://gitlab.example.com/groupA/teams/teamA/subgroupA/repoA/-/blob/branch/my/path/to/file.yaml',
|
||||
config: createConfig('0123456789'),
|
||||
response: expect.objectContaining({
|
||||
url:
|
||||
'https://gitlab.example.com/api/v4/projects/12345/repository/files/my%2Fpath%2Fto%2Ffile.yaml/raw?ref=branch',
|
||||
),
|
||||
err: undefined,
|
||||
},
|
||||
{
|
||||
target:
|
||||
'https://gitlab.com/groupA/teams/teamA/repoA/-/blob/branch/my/path/to/file.yaml', // Repo not in subgroup
|
||||
url: new URL(
|
||||
headers: expect.objectContaining({
|
||||
'private-token': '0123456789',
|
||||
}),
|
||||
}),
|
||||
},
|
||||
{
|
||||
url:
|
||||
'https://gitlab.com/groupA/teams/teamA/repoA/-/blob/branch/my/path/to/file.yaml', // Repo not in subgroup
|
||||
config: createConfig(),
|
||||
response: expect.objectContaining({
|
||||
url:
|
||||
'https://gitlab.com/api/v4/projects/12345/repository/files/my%2Fpath%2Fto%2Ffile.yaml/raw?ref=branch',
|
||||
),
|
||||
err: undefined,
|
||||
},
|
||||
];
|
||||
}),
|
||||
},
|
||||
|
||||
for (const test of tests) {
|
||||
if (test.url) {
|
||||
expect(
|
||||
processor.buildProjectUrl(test.target, 12345).toString(),
|
||||
).toEqual(test.url.toString());
|
||||
} else {
|
||||
throw new Error(
|
||||
'This should not have happened. Either err or url should have matched.',
|
||||
);
|
||||
}
|
||||
}
|
||||
// Raw URLs
|
||||
{
|
||||
url: 'https://gitlab.example.com/a/b/blob/master/c.yaml',
|
||||
config: createConfig(),
|
||||
response: expect.objectContaining({
|
||||
url: 'https://gitlab.example.com/a/b/raw/master/c.yaml',
|
||||
}),
|
||||
},
|
||||
])('should handle happy path %#', async ({ url, config, response }) => {
|
||||
const [{ reader }] = GitlabUrlReader.factory({ config, logger });
|
||||
|
||||
const data = await reader.read(url);
|
||||
const res = await JSON.parse(data.toString('utf-8'));
|
||||
expect(res).toEqual(response);
|
||||
});
|
||||
|
||||
it('should build raw urls', () => {
|
||||
const processor = new GitlabUrlReader(
|
||||
readConfig(createConfig(undefined))[0],
|
||||
);
|
||||
|
||||
const tests = [
|
||||
{
|
||||
target: 'https://gitlab.example.com/a/b/blob/master/c.yaml',
|
||||
url: new URL('https://gitlab.example.com/a/b/raw/master/c.yaml'),
|
||||
err: undefined,
|
||||
},
|
||||
];
|
||||
|
||||
for (const test of tests) {
|
||||
if (test.url) {
|
||||
expect(processor.buildRawUrl(test.target).toString()).toEqual(
|
||||
test.url.toString(),
|
||||
);
|
||||
} else {
|
||||
throw new Error(
|
||||
'This should not have happened. Either err or url should have matched.',
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should return request options', () => {
|
||||
const tests = [
|
||||
{
|
||||
token: '0123456789',
|
||||
expect: {
|
||||
headers: {
|
||||
'PRIVATE-TOKEN': '0123456789',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
token: '',
|
||||
err:
|
||||
"Invalid type in config for key 'integrations.gitlab[0].token' in '', got empty-string, wanted string",
|
||||
expect: {
|
||||
headers: {
|
||||
'PRIVATE-TOKEN': '',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
token: undefined,
|
||||
expect: {
|
||||
headers: {
|
||||
'PRIVATE-TOKEN': '',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const test of tests) {
|
||||
if (test.err) {
|
||||
expect(
|
||||
() => new GitlabUrlReader(readConfig(createConfig(test.token))[0]),
|
||||
).toThrowError(test.err);
|
||||
} else {
|
||||
const processor = new GitlabUrlReader(
|
||||
readConfig(createConfig(test.token))[0],
|
||||
);
|
||||
expect(processor.getRequestOptions()).toEqual(test.expect);
|
||||
}
|
||||
}
|
||||
it.each([
|
||||
{
|
||||
url: '',
|
||||
config: createConfig(''),
|
||||
error:
|
||||
"Invalid type in config for key 'integrations.gitlab[0].token' in 'test-config', got empty-string, wanted string",
|
||||
},
|
||||
])('should handle error path %#', async ({ url, config, error }) => {
|
||||
await expect(async () => {
|
||||
const [{ reader }] = GitlabUrlReader.factory({ config, logger });
|
||||
await reader.read(url);
|
||||
}).rejects.toThrow(error);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,7 +26,7 @@ type Options = {
|
||||
token?: string;
|
||||
};
|
||||
|
||||
export function readConfig(config: Config): Options[] {
|
||||
function readConfig(config: Config): Options[] {
|
||||
const optionsArr = Array<Options>();
|
||||
|
||||
const providerConfigs =
|
||||
@@ -59,14 +59,6 @@ export class GitlabUrlReader implements UrlReader {
|
||||
|
||||
constructor(private readonly options: Options) {}
|
||||
|
||||
getRequestOptions(): RequestInit {
|
||||
return {
|
||||
headers: {
|
||||
['PRIVATE-TOKEN']: this.options.token ?? '',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async read(url: string): Promise<Buffer> {
|
||||
// TODO(Rugvip): merged the old GitlabReaderProcessor in here and used
|
||||
// the existence of /~/blob/ to switch the logic. Don't know if this
|
||||
@@ -100,19 +92,23 @@ export class GitlabUrlReader implements UrlReader {
|
||||
// Converts
|
||||
// from: https://gitlab.example.com/a/b/blob/master/c.yaml
|
||||
// to: https://gitlab.example.com/a/b/raw/master/c.yaml
|
||||
buildRawUrl(target: string): URL {
|
||||
private buildRawUrl(target: string): URL {
|
||||
try {
|
||||
const url = new URL(target);
|
||||
|
||||
const [empty, userOrOrg, repoName, ...restOfPath] = url.pathname
|
||||
.split('/')
|
||||
// for the common case https://gitlab.example.com/a/b/-/blob/master/c.yaml
|
||||
.filter(path => path !== '-');
|
||||
const [
|
||||
empty,
|
||||
userOrOrg,
|
||||
repoName,
|
||||
blobKeyword,
|
||||
...restOfPath
|
||||
] = url.pathname.split('/');
|
||||
|
||||
if (
|
||||
empty !== '' ||
|
||||
userOrOrg === '' ||
|
||||
repoName === '' ||
|
||||
blobKeyword !== 'blob' ||
|
||||
!restOfPath.join('/').match(/\.yaml$/)
|
||||
) {
|
||||
throw new Error('Wrong GitLab URL');
|
||||
@@ -131,7 +127,7 @@ export class GitlabUrlReader implements UrlReader {
|
||||
|
||||
// convert https://gitlab.com/groupA/teams/teamA/subgroupA/repoA/-/blob/branch/filepath
|
||||
// to https://gitlab.com/api/v4/projects/<PROJECTID>/repository/files/filepath?ref=branch
|
||||
buildProjectUrl(target: string, projectID: Number): URL {
|
||||
private buildProjectUrl(target: string, projectID: Number): URL {
|
||||
try {
|
||||
const url = new URL(target);
|
||||
|
||||
@@ -154,7 +150,7 @@ export class GitlabUrlReader implements UrlReader {
|
||||
}
|
||||
}
|
||||
|
||||
async getProjectID(target: string): Promise<Number> {
|
||||
private async getProjectID(target: string): Promise<Number> {
|
||||
const url = new URL(target);
|
||||
|
||||
if (
|
||||
@@ -188,6 +184,14 @@ export class GitlabUrlReader implements UrlReader {
|
||||
}
|
||||
}
|
||||
|
||||
private getRequestOptions(): RequestInit {
|
||||
return {
|
||||
headers: {
|
||||
['PRIVATE-TOKEN']: this.options.token ?? '',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
toString() {
|
||||
const { host, token } = this.options;
|
||||
return `gitlab{host=${host},authed=${Boolean(token)}}`;
|
||||
|
||||
Reference in New Issue
Block a user