Made gitlab:issues:create action idempotent.

Signed-off-by: Bogdan Nechyporenko <bnechyporenko@bol.com>
This commit is contained in:
Bogdan Nechyporenko
2025-02-06 19:05:08 +01:00
parent edfd0d2951
commit e52cee61e9
3 changed files with 45 additions and 17 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend-module-gitlab': patch
---
Made gitlab:issues:create action idempotent.
@@ -139,17 +139,24 @@ export const createGitlabIssueAction = (options: {
let isEpicScoped = false;
if (epicId) {
isEpicScoped = await checkEpicScope(api, projectId, epicId);
isEpicScoped = await ctx.checkpoint({
key: `is.epic.scoped.${projectId}.${title}`,
fn: async () => {
if (epicId) {
isEpicScoped = await checkEpicScope(api, projectId, epicId);
if (isEpicScoped) {
ctx.logger.info('Epic is within Project Scope');
} else {
ctx.logger.warn(
'Chosen epic is not within the Project Scope. The issue will be created without an associated epic.',
);
}
}
return isEpicScoped;
},
});
if (isEpicScoped) {
ctx.logger.info('Epic is within Project Scope');
} else {
ctx.logger.warn(
'Chosen epic is not within the Project Scope. The issue will be created without an associated epic.',
);
}
}
const mappedCreatedAt = convertDate(
String(createdAt),
new Date().toISOString(),
@@ -173,11 +180,22 @@ export const createGitlabIssueAction = (options: {
weight,
};
const response = (await api.Issues.create(
projectId,
title,
issueOptions,
)) as IssueSchema;
const response = await ctx.checkpoint({
key: `issue.${projectId}.${title}`,
fn: async () => {
const issue = (await api.Issues.create(
projectId,
title,
issueOptions,
)) as IssueSchema;
return {
id: issue.id,
web_url: issue.web_url,
iid: issue.iid,
};
},
});
ctx.output('issueId', response.id);
ctx.output('issueUrl', response.web_url);
@@ -21,7 +21,7 @@ import {
mockCredentials,
mockServices,
} from '@backstage/backend-test-utils';
import { JsonObject } from '@backstage/types';
import { JsonObject, JsonValue } from '@backstage/types';
import { ActionContext } from '@backstage/plugin-scaffolder-node';
/**
@@ -43,7 +43,12 @@ export const createMockActionContext = <
output: jest.fn(),
createTemporaryDirectory: jest.fn(),
input: {} as TActionInput,
checkpoint: jest.fn(),
async checkpoint<T extends JsonValue | void>(opts: {
key: string;
fn: () => Promise<T> | T;
}): Promise<T> {
return opts.fn();
},
getInitiatorCredentials: () => Promise.resolve(credentials),
task: {
id: 'mock-task-id',