Added some patches to the plugin-scaffolder-backend-module-gitlab Part2
Signed-off-by: thomastroschke <thomas.troschke89@googlemail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend-module-gitlab': patch
|
||||
---
|
||||
|
||||
Establishing compatibility of scaffolder action gitlab:projectAccessToken:create with GitLab version ^16 by introducing the expired at parameter.
|
||||
@@ -63,6 +63,7 @@
|
||||
"@backstage/plugin-rollbar-backend": "workspace:^",
|
||||
"@backstage/plugin-scaffolder-backend": "workspace:^",
|
||||
"@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "workspace:^",
|
||||
"@backstage/plugin-scaffolder-backend-module-gitlab": "workspace:^",
|
||||
"@backstage/plugin-scaffolder-backend-module-rails": "workspace:^",
|
||||
"@backstage/plugin-search-backend": "workspace:^",
|
||||
"@backstage/plugin-search-backend-module-catalog": "workspace:^",
|
||||
|
||||
+69
-171
@@ -13,23 +13,25 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { PassThrough } from 'stream';
|
||||
import yaml from 'yaml';
|
||||
import { createGitlabProjectAccessTokenAction } from './createGitlabProjectAccessTokenAction'; // Adjust the import based on your project structure
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { PassThrough } from 'stream';
|
||||
import { examples } from './createGitlabProjectAccessTokenAction.examples';
|
||||
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
jest.mock('node-fetch');
|
||||
|
||||
const mockGitlabClient = {
|
||||
ProjectDeployTokens: {
|
||||
ProjectAccessTokens: {
|
||||
create: jest.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
jest.mock('@gitbeaker/node', () => ({
|
||||
jest.mock('@gitbeaker/rest', () => ({
|
||||
Gitlab: class {
|
||||
constructor() {
|
||||
return mockGitlabClient;
|
||||
@@ -73,231 +75,127 @@ describe('gitlab:projectAccessToken:create examples', () => {
|
||||
});
|
||||
|
||||
it('Create a GitLab project access token with minimal options.', async () => {
|
||||
const fetchMock = jest.spyOn(global, 'fetch');
|
||||
const mockResponse = {
|
||||
token: 'mock-access-token',
|
||||
};
|
||||
|
||||
fetchMock.mockResolvedValue({
|
||||
json: async () => mockResponse,
|
||||
} as Response);
|
||||
|
||||
jest.mock('../util', () => ({
|
||||
getToken: jest.fn().mockReturnValue({
|
||||
token: 'mock-api-token',
|
||||
integrationConfig: { config: { baseUrl: 'https://api.gitlab.com' } },
|
||||
}),
|
||||
}));
|
||||
mockGitlabClient.ProjectAccessTokens.create.mockResolvedValue({
|
||||
token: 'TOKEN',
|
||||
username: 'User',
|
||||
});
|
||||
|
||||
const input = yaml.parse(examples[0].example).steps[0].input;
|
||||
|
||||
await action.handler({
|
||||
...mockContext,
|
||||
input,
|
||||
});
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
'https://gitlab.com/api/v4/projects/456/access_tokens',
|
||||
expect(mockGitlabClient.ProjectAccessTokens.create).toHaveBeenCalledWith(
|
||||
'456',
|
||||
'tokenname',
|
||||
['read_repository'],
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'PRIVATE-TOKEN': 'tokenlols',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: undefined,
|
||||
scopes: undefined,
|
||||
access_level: undefined,
|
||||
}),
|
||||
accessLevel: 40,
|
||||
expiresAt: DateTime.now().plus({ days: 365 }).toISODate()!,
|
||||
},
|
||||
);
|
||||
expect(mockContext.output).toHaveBeenCalledWith(
|
||||
'access_token',
|
||||
'mock-access-token',
|
||||
);
|
||||
|
||||
expect(mockContext.output).toHaveBeenCalledWith('access_token', 'TOKEN');
|
||||
});
|
||||
|
||||
it('Create a GitLab project access token with custom scopes.', async () => {
|
||||
mockGitlabClient.ProjectAccessTokens.create.mockResolvedValue({
|
||||
token: 'TOKEN',
|
||||
username: 'User',
|
||||
});
|
||||
|
||||
const input = yaml.parse(examples[1].example).steps[0].input;
|
||||
|
||||
const mockResponse = {
|
||||
token: 'mock-access-token',
|
||||
};
|
||||
|
||||
const fetchMock = jest.spyOn(global, 'fetch');
|
||||
fetchMock.mockResolvedValue({
|
||||
json: async () => mockResponse,
|
||||
} as Response);
|
||||
|
||||
jest.mock('../util', () => ({
|
||||
getToken: jest.fn().mockReturnValue({
|
||||
token: 'mock-api-token',
|
||||
integrationConfig: { config: { baseUrl: 'https://api.gitlab.com' } },
|
||||
}),
|
||||
}));
|
||||
|
||||
await action.handler({
|
||||
...mockContext,
|
||||
input,
|
||||
});
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
'https://gitlab.com/api/v4/projects/789/access_tokens',
|
||||
expect(mockGitlabClient.ProjectAccessTokens.create).toHaveBeenCalledWith(
|
||||
'789',
|
||||
'tokenname',
|
||||
['read_registry', 'write_repository'],
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'PRIVATE-TOKEN': 'tokenlols',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: undefined,
|
||||
scopes: ['read_registry', 'write_repository'],
|
||||
access_level: undefined,
|
||||
}),
|
||||
accessLevel: 40,
|
||||
expiresAt: DateTime.now().plus({ days: 365 }).toISODate()!,
|
||||
},
|
||||
);
|
||||
|
||||
expect(mockContext.output).toHaveBeenCalledWith(
|
||||
'access_token',
|
||||
'mock-access-token',
|
||||
);
|
||||
expect(mockContext.output).toHaveBeenCalledWith('access_token', 'TOKEN');
|
||||
});
|
||||
|
||||
it('Create a GitLab project access token with a specified name.', async () => {
|
||||
mockGitlabClient.ProjectAccessTokens.create.mockResolvedValue({
|
||||
token: 'TOKEN',
|
||||
username: 'User',
|
||||
});
|
||||
|
||||
const input = yaml.parse(examples[2].example).steps[0].input;
|
||||
|
||||
const mockResponse = {
|
||||
token: 'mock-access-token',
|
||||
};
|
||||
|
||||
const fetchMock = jest.spyOn(global, 'fetch');
|
||||
fetchMock.mockResolvedValue({
|
||||
json: async () => mockResponse,
|
||||
} as Response);
|
||||
|
||||
jest.mock('../util', () => ({
|
||||
getToken: jest.fn().mockReturnValue({
|
||||
token: 'mock-api-token',
|
||||
integrationConfig: { config: { baseUrl: 'https://api.gitlab.com' } },
|
||||
}),
|
||||
}));
|
||||
|
||||
await action.handler({
|
||||
...mockContext,
|
||||
input,
|
||||
});
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
'https://gitlab.com/api/v4/projects/101112/access_tokens',
|
||||
expect(mockGitlabClient.ProjectAccessTokens.create).toHaveBeenCalledWith(
|
||||
'101112',
|
||||
'my-custom-token',
|
||||
['read_repository'],
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'PRIVATE-TOKEN': 'tokenlols',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: 'my-custom-token',
|
||||
scopes: undefined,
|
||||
access_level: undefined,
|
||||
}),
|
||||
accessLevel: 40,
|
||||
expiresAt: DateTime.now().plus({ days: 365 }).toISODate()!,
|
||||
},
|
||||
);
|
||||
|
||||
expect(mockContext.output).toHaveBeenCalledWith(
|
||||
'access_token',
|
||||
'mock-access-token',
|
||||
);
|
||||
expect(mockContext.output).toHaveBeenCalledWith('access_token', 'TOKEN');
|
||||
});
|
||||
|
||||
it('Create a GitLab project access token with a numeric project ID.', async () => {
|
||||
mockGitlabClient.ProjectAccessTokens.create.mockResolvedValue({
|
||||
token: 'TOKEN',
|
||||
username: 'User',
|
||||
});
|
||||
|
||||
const input = yaml.parse(examples[3].example).steps[0].input;
|
||||
|
||||
const mockResponse = {
|
||||
token: 'mock-access-token',
|
||||
};
|
||||
|
||||
const fetchMock = jest.spyOn(global, 'fetch');
|
||||
fetchMock.mockResolvedValue({
|
||||
json: async () => mockResponse,
|
||||
} as Response);
|
||||
|
||||
jest.mock('../util', () => ({
|
||||
getToken: jest.fn().mockReturnValue({
|
||||
token: 'mock-api-token',
|
||||
integrationConfig: { config: { baseUrl: 'https://api.gitlab.com' } },
|
||||
}),
|
||||
}));
|
||||
|
||||
await action.handler({
|
||||
...mockContext,
|
||||
input,
|
||||
});
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
'https://gitlab.com/api/v4/projects/42/access_tokens',
|
||||
expect(mockGitlabClient.ProjectAccessTokens.create).toHaveBeenCalledWith(
|
||||
42,
|
||||
'tokenname',
|
||||
['read_repository'],
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'PRIVATE-TOKEN': 'tokenlols',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: undefined,
|
||||
scopes: undefined,
|
||||
access_level: undefined,
|
||||
}),
|
||||
accessLevel: 40,
|
||||
expiresAt: DateTime.now().plus({ days: 365 }).toISODate()!,
|
||||
},
|
||||
);
|
||||
|
||||
expect(mockContext.output).toHaveBeenCalledWith(
|
||||
'access_token',
|
||||
'mock-access-token',
|
||||
);
|
||||
expect(mockContext.output).toHaveBeenCalledWith('access_token', 'TOKEN');
|
||||
});
|
||||
|
||||
it('Create a GitLab project access token using specific GitLab integrations.', async () => {
|
||||
it('Create a GitLab project access token with a specified expired Date.', async () => {
|
||||
mockGitlabClient.ProjectAccessTokens.create.mockResolvedValue({
|
||||
token: 'TOKEN',
|
||||
username: 'User',
|
||||
});
|
||||
|
||||
const input = yaml.parse(examples[4].example).steps[0].input;
|
||||
|
||||
const mockResponse = {
|
||||
token: 'mock-access-token',
|
||||
};
|
||||
|
||||
const fetchMock = jest.spyOn(global, 'fetch');
|
||||
fetchMock.mockResolvedValue({
|
||||
json: async () => mockResponse,
|
||||
} as Response);
|
||||
|
||||
jest.mock('../util', () => ({
|
||||
getToken: jest.fn().mockReturnValue({
|
||||
token: 'mock-api-token',
|
||||
integrationConfig: { config: { baseUrl: 'https://api.gitlab.com' } },
|
||||
}),
|
||||
}));
|
||||
|
||||
await action.handler({
|
||||
...mockContext,
|
||||
input,
|
||||
});
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
'https://gitlab.com/api/v4/projects/123/access_tokens',
|
||||
expect(mockGitlabClient.ProjectAccessTokens.create).toHaveBeenCalledWith(
|
||||
'123',
|
||||
'tokenname',
|
||||
['read_repository'],
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'PRIVATE-TOKEN': 'tokenlols',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: undefined,
|
||||
scopes: undefined,
|
||||
access_level: undefined,
|
||||
}),
|
||||
accessLevel: 40,
|
||||
expiresAt: '2024-06-25',
|
||||
},
|
||||
);
|
||||
|
||||
expect(mockContext.output).toHaveBeenCalledWith(
|
||||
'access_token',
|
||||
'mock-access-token',
|
||||
);
|
||||
expect(mockContext.output).toHaveBeenCalledWith('access_token', 'TOKEN');
|
||||
});
|
||||
});
|
||||
|
||||
+2
-1
@@ -86,7 +86,7 @@ export const examples: TemplateExample[] = [
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Create a GitLab project access token using specific GitLab integrations.',
|
||||
'Create a GitLab project access token with a specified expired Date.',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
@@ -96,6 +96,7 @@ export const examples: TemplateExample[] = [
|
||||
input: {
|
||||
repoUrl: 'gitlab.com?repo=repo&owner=owner',
|
||||
projectId: '123',
|
||||
expiresAt: '2024-06-25',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
+76
-30
@@ -14,11 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
|
||||
import { InputError } from '@backstage/errors';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
import commonGitlabConfig from '../commonGitlabConfig';
|
||||
import { getToken } from '../util';
|
||||
import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
|
||||
import { AccessTokenScopes } from '@gitbeaker/core';
|
||||
import { Gitlab } from '@gitbeaker/rest';
|
||||
import { DateTime } from 'luxon';
|
||||
import { z } from 'zod';
|
||||
import { getToken } from '../util';
|
||||
import { examples } from './createGitlabProjectAccessTokenAction.examples';
|
||||
|
||||
/**
|
||||
@@ -27,6 +30,9 @@ import { examples } from './createGitlabProjectAccessTokenAction.examples';
|
||||
* @param options - Templating configuration.
|
||||
* @public
|
||||
*/
|
||||
|
||||
const gitbeakerAccessTokenScopesType: z.ZodType<AccessTokenScopes[]> = z.any();
|
||||
|
||||
export const createGitlabProjectAccessTokenAction = (options: {
|
||||
integrations: ScmIntegrationRegistry;
|
||||
}) => {
|
||||
@@ -35,46 +41,86 @@ export const createGitlabProjectAccessTokenAction = (options: {
|
||||
id: 'gitlab:projectAccessToken:create',
|
||||
examples,
|
||||
schema: {
|
||||
input: commonGitlabConfig.merge(
|
||||
z.object({
|
||||
projectId: z.union([z.number(), z.string()], {
|
||||
description: 'Project ID',
|
||||
}),
|
||||
name: z.string({ description: 'Deploy Token Name' }).optional(),
|
||||
accessLevel: z
|
||||
.number({ description: 'Access Level of the Token' })
|
||||
.optional(),
|
||||
scopes: z.array(z.string(), { description: 'Scopes' }).optional(),
|
||||
input: z.object({
|
||||
projectId: z.number({
|
||||
description: 'Project ID/Name(slug) of the Gitlab Project',
|
||||
}),
|
||||
),
|
||||
name: z.string({ description: 'Name of Access Key' }).optional(),
|
||||
repoUrl: z.string({ description: 'URL to gitlab instance' }),
|
||||
accessLevel: z
|
||||
.number({
|
||||
description:
|
||||
'Access Level of the Token, 10 (Guest), 20 (Reporter), 30 (Developer), 40 (Maintainer), and 50 (Owner)',
|
||||
})
|
||||
.optional(),
|
||||
scopes: gitbeakerAccessTokenScopesType,
|
||||
expiresAt: z
|
||||
.string({
|
||||
description:
|
||||
'Expiration date of the access token in ISO format (YYYY-MM-DD). If Empty, it will set to the maximum of 365 days.',
|
||||
})
|
||||
.optional(),
|
||||
token: z
|
||||
.string({
|
||||
description: 'The token to use for authorization to GitLab',
|
||||
})
|
||||
.optional(),
|
||||
}),
|
||||
output: z.object({
|
||||
access_token: z.string({ description: 'Access Token' }),
|
||||
}),
|
||||
},
|
||||
async handler(ctx) {
|
||||
ctx.logger.info(`Creating Token for Project "${ctx.input.projectId}"`);
|
||||
const { projectId, name, accessLevel, scopes } = ctx.input;
|
||||
const {
|
||||
projectId,
|
||||
name = 'tokenname',
|
||||
accessLevel = 40,
|
||||
scopes = ['read_repository'],
|
||||
expiresAt,
|
||||
} = ctx.input;
|
||||
|
||||
const { token, integrationConfig } = getToken(ctx.input, integrations);
|
||||
|
||||
const response = await fetch(
|
||||
`${integrationConfig.config.baseUrl}/api/v4/projects/${projectId}/access_tokens`,
|
||||
if (!integrationConfig.config.token && token) {
|
||||
throw new InputError(
|
||||
`No token available for host ${integrationConfig.config.baseUrl}`,
|
||||
);
|
||||
}
|
||||
|
||||
let api;
|
||||
|
||||
if (!ctx.input.token) {
|
||||
api = new Gitlab({
|
||||
host: integrationConfig.config.baseUrl,
|
||||
token: token,
|
||||
});
|
||||
} else {
|
||||
api = new Gitlab({
|
||||
host: integrationConfig.config.baseUrl,
|
||||
oauthToken: token,
|
||||
});
|
||||
}
|
||||
|
||||
const mappedAccessLevel = Number(accessLevel.valueOf()) as AccessLevel;
|
||||
|
||||
const response = await api.ProjectAccessTokens.create(
|
||||
projectId,
|
||||
name,
|
||||
scopes,
|
||||
{
|
||||
method: 'POST', // *GET, POST, PUT, DELETE, etc.
|
||||
headers: {
|
||||
'PRIVATE-TOKEN': token,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: name,
|
||||
scopes: scopes,
|
||||
access_level: accessLevel,
|
||||
}),
|
||||
expiresAt:
|
||||
expiresAt || DateTime.now().plus({ days: 365 }).toISODate()!,
|
||||
accessLevel: mappedAccessLevel,
|
||||
},
|
||||
);
|
||||
if (!response.token) {
|
||||
throw new Error('Could not create project access token');
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
ctx.output('access_token', result.token);
|
||||
ctx.output('access_token', response.token);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
declare type AccessLevel = 0 | 5 | 10 | 20 | 30 | 40 | 50;
|
||||
|
||||
@@ -8385,6 +8385,7 @@ __metadata:
|
||||
"@gitbeaker/node": ^35.8.0
|
||||
"@gitbeaker/rest": ^39.25.0
|
||||
jest-date-mock: ^1.0.8
|
||||
luxon: ^3.4.4
|
||||
yaml: ^2.0.0
|
||||
zod: ^3.22.4
|
||||
languageName: unknown
|
||||
@@ -26885,6 +26886,7 @@ __metadata:
|
||||
"@backstage/plugin-rollbar-backend": "workspace:^"
|
||||
"@backstage/plugin-scaffolder-backend": "workspace:^"
|
||||
"@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "workspace:^"
|
||||
"@backstage/plugin-scaffolder-backend-module-gitlab": "workspace:^"
|
||||
"@backstage/plugin-scaffolder-backend-module-rails": "workspace:^"
|
||||
"@backstage/plugin-search-backend": "workspace:^"
|
||||
"@backstage/plugin-search-backend-module-catalog": "workspace:^"
|
||||
@@ -33297,7 +33299,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"luxon@npm:^3.0.0, luxon@npm:^3.3.0, luxon@npm:^3.4.3":
|
||||
"luxon@npm:^3.0.0, luxon@npm:^3.3.0, luxon@npm:^3.4.3, luxon@npm:^3.4.4":
|
||||
version: 3.4.4
|
||||
resolution: "luxon@npm:3.4.4"
|
||||
checksum: 36c1f99c4796ee4bfddf7dc94fa87815add43ebc44c8934c924946260a58512f0fd2743a629302885df7f35ccbd2d13f178c15df046d0e3b6eb71db178f1c60c
|
||||
|
||||
Reference in New Issue
Block a user