feat: support pathes in gitlab:group:ensureExists (#29399)

Signed-off-by: Johannes Will <johannes.will@siemens.com>
This commit is contained in:
Johannes Will
2025-03-26 14:14:38 +01:00
parent a31a430c17
commit 003dc1563f
5 changed files with 126 additions and 5 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend-module-gitlab': patch
---
Updated the path field in the `gitlab:group:ensureExists` action to suport also strings with multiple segments (e.g. group/subgroup)
@@ -243,4 +243,55 @@ describe('gitlab:group:ensureExists', () => {
expect(mockContext.output).toHaveBeenCalledWith('groupId', 3);
});
it(`Should ${examples[5].description}`, async () => {
mockGitlabClient.Groups.search.mockResolvedValue([
{
id: 1,
full_path: 'group1',
},
{
id: 2,
full_path: 'group1/group2',
},
{
id: 3,
full_path: 'group1/group2/group3',
},
]);
mockGitlabClient.Groups.create.mockResolvedValue({
id: 4,
full_path: 'group1/group2/group3/group4',
});
const config = new ConfigReader({
integrations: {
gitlab: [
{
host: 'gitlab.com',
token: 'tokenlols',
apiBaseUrl: 'https://api.gitlab.com',
},
],
},
});
const integrations = ScmIntegrations.fromConfig(config);
const action = createGitlabGroupEnsureExistsAction({ integrations });
await action.handler({
...mockContext,
input: yaml.parse(examples[5].example).steps[0].input,
});
expect(mockGitlabClient.Groups.create).toHaveBeenCalledWith(
'Group 4',
'group4',
{
parentId: 3,
},
);
expect(mockContext.output).toHaveBeenCalledWith('groupId', 4);
});
});
@@ -102,4 +102,25 @@ export const examples: TemplateExample[] = [
],
}),
},
{
description:
'Create a group nested within another group using path and objects',
example: yaml.stringify({
steps: [
{
id: 'gitlabGroup',
name: 'Group',
action: 'gitlab:group:ensureExists',
input: {
repoUrl: 'gitlab.com',
path: [
'group1/group2',
{ name: 'Group 3', slug: 'group3' },
{ name: 'Group 4', slug: 'group4' },
],
},
},
],
}),
},
];
@@ -94,6 +94,38 @@ describe('gitlab:group:ensureExists', () => {
expect(mockContext.output).toHaveBeenCalledWith('groupId', 3);
});
it('should create a new group from pathstring if it does not exists', async () => {
mockGitlabClient.Groups.search.mockResolvedValue([
{
id: 1,
full_path: 'bar',
},
{
id: 2,
full_path: 'foo',
},
]);
mockGitlabClient.Groups.create.mockResolvedValue({
id: 3,
full_path: 'foo/bar',
});
await action.handler({
...mockContext,
input: {
repoUrl: 'gitlab.com?repo=repo&owner=owner',
path: ['foo/bar', 'baz'],
},
});
expect(mockGitlabClient.Groups.create).toHaveBeenCalledWith('bar', 'bar', {
parentId: 2,
});
expect(mockContext.output).toHaveBeenCalledWith('groupId', 3);
});
it('should create a new group from object if it does not exists', async () => {
mockGitlabClient.Groups.search.mockResolvedValue([
{
@@ -76,11 +76,7 @@ export const createGitlabGroupEnsureExistsAction = (options: {
let currentPath: string | null = null;
let parentId: number | null = null;
for (const pathElement of path) {
const slug =
typeof pathElement === 'string' ? pathElement : pathElement.slug;
const name =
typeof pathElement === 'string' ? pathElement : pathElement.name;
for (const { name, slug } of pathIterator(path)) {
const fullPath: string = currentPath ? `${currentPath}/${slug}` : slug;
const result = (await api.Groups.search(
fullPath,
@@ -119,3 +115,19 @@ export const createGitlabGroupEnsureExistsAction = (options: {
},
});
};
type PathPart = { name: string; slug: string };
type PathItem = string | PathPart;
function* pathIterator(items: PathItem[]): Generator<PathPart> {
for (const item of items) {
if (typeof item === 'string') {
const parts = item.split('/');
for (const part of parts) {
yield { name: part, slug: part };
}
} else {
yield item;
}
}
}