made move operation transactional

This commit is contained in:
Sheen Capadngan
2024-07-09 16:03:50 +08:00
parent a06dee66f8
commit 05bf2e4696
3 changed files with 118 additions and 110 deletions

View File

@@ -1338,12 +1338,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
sourceSecretPath: z.string().trim().default("/").transform(removeTrailingSlash), sourceSecretPath: z.string().trim().default("/").transform(removeTrailingSlash),
destinationEnvironment: z.string().trim(), destinationEnvironment: z.string().trim(),
destinationSecretPath: z.string().trim().default("/").transform(removeTrailingSlash), destinationSecretPath: z.string().trim().default("/").transform(removeTrailingSlash),
secrets: z secretIds: z.string().array()
.object({
id: z.string()
})
.array()
.min(1)
}), }),
response: { response: {
200: z.union([ 200: z.union([

View File

@@ -1703,7 +1703,7 @@ export const secretServiceFactory = ({
sourceSecretPath, sourceSecretPath,
destinationEnvironment, destinationEnvironment,
destinationSecretPath, destinationSecretPath,
secrets, secretIds,
projectSlug, projectSlug,
actor, actor,
actorId, actorId,
@@ -1767,11 +1767,11 @@ export const secretServiceFactory = ({
const sourceSecrets = await secretDAL.find({ const sourceSecrets = await secretDAL.find({
type: SecretType.Shared, type: SecretType.Shared,
$in: { $in: {
id: secrets.map((secret) => secret.id) id: secretIds
} }
}); });
if (sourceSecrets.length !== secrets.length) { if (sourceSecrets.length !== secretIds.length) {
throw new BadRequestError({ throw new BadRequestError({
message: "Invalid secrets" message: "Invalid secrets"
}); });
@@ -1793,11 +1793,18 @@ export const secretServiceFactory = ({
}) })
})); }));
let isSourceFolderUpdated = false;
let isDestinationFolderUpdated = false;
// Moving secrets is a two-step process. // Moving secrets is a two-step process.
await secretDAL.transaction(async (tx) => {
// First step is to create/update the secret in the destination: // First step is to create/update the secret in the destination:
const destinationSecretsFromDB = await secretDAL.find({ const destinationSecretsFromDB = await secretDAL.find(
{
folderId: destinationFolder.id folderId: destinationFolder.id
}); },
{ tx }
);
const decryptedDestinationSecrets = destinationSecretsFromDB.map((secret) => { const decryptedDestinationSecrets = destinationSecretsFromDB.map((secret) => {
return { return {
@@ -1853,9 +1860,12 @@ export const secretServiceFactory = ({
if (destinationFolderPolicy && actor === ActorType.USER) { if (destinationFolderPolicy && actor === ActorType.USER) {
// if secret approval policy exists for destination, we create the secret approval request // if secret approval policy exists for destination, we create the secret approval request
const localSecretsIds = decryptedDestinationSecrets.map(({ id }) => id); const localSecretsIds = decryptedDestinationSecrets.map(({ id }) => id);
const latestSecretVersions = await secretVersionDAL.findLatestVersionMany(destinationFolder.id, localSecretsIds); const latestSecretVersions = await secretVersionDAL.findLatestVersionMany(
destinationFolder.id,
localSecretsIds,
tx
);
await secretApprovalRequestDAL.transaction(async (tx) => {
const approvalRequestDoc = await secretApprovalRequestDAL.create( const approvalRequestDoc = await secretApprovalRequestDAL.create(
{ {
folderId: destinationFolder.id, folderId: destinationFolder.id,
@@ -1895,12 +1905,9 @@ export const secretServiceFactory = ({
: {}) : {})
}; };
}); });
const approvalCommits = await secretApprovalRequestSecretDAL.insertMany(commits, tx); await secretApprovalRequestSecretDAL.insertMany(commits, tx);
return { ...approvalRequestDoc, commits: approvalCommits };
});
} else { } else {
// apply changes directly // apply changes directly
await secretDAL.transaction(async (tx) => {
if (locallyCreatedSecrets.length) { if (locallyCreatedSecrets.length) {
await fnSecretBulkInsert({ await fnSecretBulkInsert({
folderId: destinationFolder.id, folderId: destinationFolder.id,
@@ -1967,15 +1974,7 @@ export const secretServiceFactory = ({
}); });
} }
await snapshotService.performSnapshot(destinationFolder.id); isDestinationFolderUpdated = true;
await secretQueueService.syncSecrets({
projectId: project.id,
secretPath: destinationFolder.path,
environmentSlug: destinationFolder.environment.slug,
actorId,
actor
});
});
} }
// Next step is to delete the secrets from the source folder: // Next step is to delete the secrets from the source folder:
@@ -1991,9 +1990,7 @@ export const secretServiceFactory = ({
if (sourceFolderPolicy && actor === ActorType.USER) { if (sourceFolderPolicy && actor === ActorType.USER) {
// if secret approval policy exists for source, we create the secret approval request // if secret approval policy exists for source, we create the secret approval request
const localSecretsIds = decryptedSourceSecrets.map(({ id }) => id); const localSecretsIds = decryptedSourceSecrets.map(({ id }) => id);
const latestSecretVersions = await secretVersionDAL.findLatestVersionMany(sourceFolder.id, localSecretsIds); const latestSecretVersions = await secretVersionDAL.findLatestVersionMany(sourceFolder.id, localSecretsIds, tx);
await secretApprovalRequestDAL.transaction(async (tx) => {
const approvalRequestDoc = await secretApprovalRequestDAL.create( const approvalRequestDoc = await secretApprovalRequestDAL.create(
{ {
folderId: sourceFolder.id, folderId: sourceFolder.id,
@@ -2031,18 +2028,36 @@ export const secretServiceFactory = ({
secretVersion: latestSecretVersions[localSecret.id].id secretVersion: latestSecretVersions[localSecret.id].id
}; };
}); });
const approvalCommits = await secretApprovalRequestSecretDAL.insertMany(commits, tx);
return { ...approvalRequestDoc, commits: approvalCommits }; await secretApprovalRequestSecretDAL.insertMany(commits, tx);
});
} else { } else {
// if no secret approval policy is present, we delete directly. // if no secret approval policy is present, we delete directly.
await secretDAL.delete({ await secretDAL.delete(
{
$in: { $in: {
id: locallyDeletedSecrets.map(({ id }) => id) id: locallyDeletedSecrets.map(({ id }) => id)
}, },
folderId: sourceFolder.id folderId: sourceFolder.id
},
tx
);
isSourceFolderUpdated = true;
}
}); });
if (isDestinationFolderUpdated) {
await snapshotService.performSnapshot(destinationFolder.id);
await secretQueueService.syncSecrets({
projectId: project.id,
secretPath: destinationFolder.path,
environmentSlug: destinationFolder.environment.slug,
actorId,
actor
});
}
if (isSourceFolderUpdated) {
await snapshotService.performSnapshot(sourceFolder.id); await snapshotService.performSnapshot(sourceFolder.id);
await secretQueueService.syncSecrets({ await secretQueueService.syncSecrets({
projectId: project.id, projectId: project.id,

View File

@@ -404,7 +404,5 @@ export type TMoveSecretsDTO = {
sourceSecretPath: string; sourceSecretPath: string;
destinationEnvironment: string; destinationEnvironment: string;
destinationSecretPath: string; destinationSecretPath: string;
secrets: { secretIds: string[];
id: string;
}[];
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;