PIT: general improvements and fixes

This commit is contained in:
carlosmonastyrski
2025-05-25 00:12:31 -03:00
parent 28a3bf0b94
commit b7b90aea33
21 changed files with 1588 additions and 896 deletions
@@ -38,7 +38,7 @@ export enum CommitType {
DELETE = "delete"
}
enum ResourceType {
export enum ResourceType {
SECRET = "secret",
FOLDER = "folder"
}
@@ -68,8 +68,7 @@ type TCommitChangeDTO = {
folderVersionId?: string;
};
export type ResourceChange = {
type: string;
type BaseChange = {
id: string;
versionId: string;
oldVersionId?: string;
@@ -78,12 +77,14 @@ export type ResourceChange = {
createdAt?: Date;
parentId?: string;
isUpdate?: boolean;
fromVersion?: string;
};
type SecretChange = {
type: ResourceType.SECRET;
secretKey?: string;
secretVersion?: string;
secretId?: string;
folderName?: string;
folderVersion?: string;
fromVersion?: string;
versions?: {
secretKey?: string;
secretComment?: string;
@@ -94,10 +95,20 @@ export type ResourceChange = {
tags?: string[] | null;
secretReminderRecipients?: string[] | null;
secretValue?: string;
name?: string;
}[];
};
type FolderChange = {
type: ResourceType.FOLDER;
folderName?: string;
folderVersion?: string;
versions?: {
name: string;
}[];
};
export type ResourceChange = BaseChange & (SecretChange | FolderChange);
type ActorInfo = {
actorType: string;
actorId?: string;
@@ -150,7 +161,7 @@ export const folderCommitServiceFactory = ({
}: TFolderCommitServiceFactoryDep) => {
const appCfg = getConfig();
const checkProjectPermission = async ({
const checkProjectCommitReadPermission = async ({
actor,
actorId,
projectId,
@@ -377,7 +388,7 @@ export const folderCommitServiceFactory = ({
targetCommitId: string;
defaultOperation?: "create" | "update" | "delete";
tx?: Knex;
}) => {
}): Promise<ResourceChange[]> => {
const targetCommit = await folderCommitDAL.findById(targetCommitId, tx);
if (!targetCommit) {
throw new NotFoundError({ message: `Commit with ID ${targetCommitId} not found` });
@@ -387,17 +398,34 @@ export const folderCommitServiceFactory = ({
if (!currentCommitId) {
const targetState = await reconstructFolderState(targetCommitId, tx);
return targetState.map((resource) => ({
type: resource.type,
id: resource.id,
versionId: resource.versionId,
changeType: defaultOperation,
commitId: targetCommit.commitId,
secretKey: resource.secretKey,
secretVersion: resource.secretVersion,
folderName: resource.folderName,
folderVersion: resource.folderVersion
})) as ResourceChange[];
return targetState
.map((resource): ResourceChange | null => {
if (resource.type === ResourceType.SECRET) {
return {
type: ResourceType.SECRET,
id: resource.id,
versionId: resource.versionId,
changeType: defaultOperation as ChangeType,
commitId: targetCommit.commitId,
secretKey: resource.secretKey,
secretVersion: resource.secretVersion,
secretId: resource.id
};
}
if (resource.type === ResourceType.FOLDER) {
return {
type: ResourceType.FOLDER,
id: resource.id,
versionId: resource.versionId,
changeType: defaultOperation as ChangeType,
commitId: targetCommit.commitId,
folderName: resource.folderName,
folderVersion: resource.folderVersion
};
}
return null;
})
.filter((change): change is ResourceChange => !!change);
}
// Original logic for when currentCommitId is provided
@@ -452,49 +480,88 @@ export const folderCommitServiceFactory = ({
const targetResource = targetMap[key];
if (!targetResource) {
differences.push({
type: currentResource.type,
id: currentResource.id,
versionId: currentResource.versionId,
changeType: ChangeType.DELETE,
commitId: targetCommit.commitId,
secretKey: currentResource.secretKey,
secretVersion: currentResource.secretVersion,
folderName: currentResource.folderName,
folderVersion: currentResource.folderVersion,
fromVersion: currentResource.versionId
});
// Resource was deleted
if (currentResource.type === ResourceType.SECRET) {
differences.push({
type: ResourceType.SECRET,
id: currentResource.id,
versionId: currentResource.versionId,
changeType: ChangeType.DELETE,
commitId: targetCommit.commitId,
secretKey: currentResource.secretKey,
secretVersion: currentResource.secretVersion,
secretId: currentResource.id,
fromVersion: currentResource.versionId
});
} else if (currentResource.type === ResourceType.FOLDER) {
differences.push({
type: ResourceType.FOLDER,
id: currentResource.id,
versionId: currentResource.versionId,
changeType: ChangeType.DELETE,
commitId: targetCommit.commitId,
folderName: currentResource.folderName,
folderVersion: currentResource.folderVersion,
fromVersion: currentResource.versionId
});
}
} else if (currentResource.versionId !== targetResource.versionId) {
differences.push({
type: targetResource.type,
id: targetResource.id,
versionId: targetResource.versionId,
changeType: ChangeType.UPDATE,
commitId: targetCommit.commitId,
secretKey: targetResource.secretKey,
secretVersion: targetResource.secretVersion,
folderName: targetResource.folderName,
folderVersion: targetResource.folderVersion,
fromVersion: currentResource.folderVersion || currentResource.secretVersion
});
// Resource was updated
if (targetResource.type === ResourceType.SECRET) {
differences.push({
type: ResourceType.SECRET,
id: targetResource.id,
versionId: targetResource.versionId,
changeType: ChangeType.UPDATE,
commitId: targetCommit.commitId,
secretKey: targetResource.secretKey,
secretVersion: targetResource.secretVersion,
secretId: targetResource.id,
fromVersion: currentResource.secretVersion
});
} else if (targetResource.type === ResourceType.FOLDER) {
differences.push({
type: ResourceType.FOLDER,
id: targetResource.id,
versionId: targetResource.versionId,
changeType: ChangeType.UPDATE,
commitId: targetCommit.commitId,
folderName: targetResource.folderName,
folderVersion: targetResource.folderVersion,
fromVersion: currentResource.folderVersion
});
}
}
});
// Find new resources
Object.keys(targetMap).forEach((key) => {
if (!currentMap[key]) {
const targetResource = targetMap[key];
differences.push({
type: targetResource.type,
id: targetResource.id,
versionId: targetResource.versionId,
changeType: ChangeType.CREATE,
commitId: targetCommit.commitId,
createdAt: targetCommit.createdAt,
secretKey: targetResource.secretKey,
secretVersion: targetResource.secretVersion,
folderName: targetResource.folderName,
folderVersion: targetResource.folderVersion
});
if (targetResource.type === ResourceType.SECRET) {
differences.push({
type: ResourceType.SECRET,
id: targetResource.id,
versionId: targetResource.versionId,
changeType: ChangeType.CREATE,
commitId: targetCommit.commitId,
createdAt: targetCommit.createdAt,
secretKey: targetResource.secretKey,
secretVersion: targetResource.secretVersion,
secretId: targetResource.id
});
} else if (targetResource.type === ResourceType.FOLDER) {
differences.push({
type: ResourceType.FOLDER,
id: targetResource.id,
versionId: targetResource.versionId,
changeType: ChangeType.CREATE,
commitId: targetCommit.commitId,
createdAt: targetCommit.createdAt,
folderName: targetResource.folderName,
folderVersion: targetResource.folderVersion
});
}
}
});
@@ -640,13 +707,18 @@ export const folderCommitServiceFactory = ({
) => {
const commitChanges = [];
// Filter only secret changes using discriminated union
const secretChanges = changes.filter(
(change): change is ResourceChange & SecretChange => change.type === ResourceType.SECRET
);
// Collect all secretIds for batch lookup
const secretIds = changes.map((change) => secretVersions[change.id]?.secretId).filter(Boolean);
const secretIds = secretChanges.map((change) => secretVersions[change.id]?.secretId).filter(Boolean);
// Fetch all latest versions in one call
const latestVersionsMap = await secretVersionV2BridgeDAL.findLatestVersionMany(folderId, secretIds, tx);
for (const change of changes) {
for (const change of secretChanges) {
const secretVersion = secretVersions[change.id];
// eslint-disable-next-line no-continue
if (!secretVersion) continue;
@@ -800,7 +872,12 @@ export const folderCommitServiceFactory = ({
) => {
const commitChanges = [];
for (const change of changes) {
// Filter only folder changes using discriminated union
const folderChanges = changes.filter(
(change): change is ResourceChange & FolderChange => change.type === ResourceType.FOLDER
);
for (const change of folderChanges) {
const folderVersion = folderVersions[change.id];
switch (change.changeType) {
@@ -913,9 +990,14 @@ export const folderCommitServiceFactory = ({
return commitChanges;
};
// Group differences by type for more efficient processing
const secretChanges = differences.filter((diff) => diff.type === ResourceType.SECRET);
const folderChanges = differences.filter((diff) => diff.type === ResourceType.FOLDER);
// Group differences by type for more efficient processing using discriminated unions
const secretChanges = differences.filter(
(diff): diff is ResourceChange & SecretChange => diff.type === ResourceType.SECRET
);
const folderChanges = differences.filter(
(diff): diff is ResourceChange & FolderChange => diff.type === ResourceType.FOLDER
);
// Batch fetch necessary data
const secretVersions = await secretVersionV2BridgeDAL.findByIdsWithLatestVersion(
@@ -933,8 +1015,8 @@ export const folderCommitServiceFactory = ({
// Process changes in parallel
const [secretCommitChanges, folderCommitChanges] = await Promise.all([
processSecretChanges(secretChanges, secretVersions, actorInfo, folderId, tx),
processFolderChanges(folderChanges, folderVersions)
processSecretChanges(differences, secretVersions, actorInfo, folderId, tx),
processFolderChanges(differences, folderVersions)
]);
// Combine all changes
@@ -988,8 +1070,31 @@ export const folderCommitServiceFactory = ({
/**
* Retrieve a commit by ID
*/
const getCommitById = async (id: string, tx?: Knex) => {
return folderCommitDAL.findById(id, tx);
const getCommitById = async ({
commitId,
actor,
actorId,
actorAuthMethod,
actorOrgId,
projectId,
tx
}: {
commitId: string;
actor: ActorType;
actorId: string;
actorAuthMethod: ActorAuthMethod;
actorOrgId: string;
projectId: string;
tx?: Knex;
}) => {
await checkProjectCommitReadPermission({
actor,
actorId,
actorAuthMethod,
actorOrgId,
projectId
});
return folderCommitDAL.findById(commitId, tx);
};
/**
@@ -1024,7 +1129,7 @@ export const folderCommitServiceFactory = ({
search?: string;
sort: "asc" | "desc";
}) => {
await checkProjectPermission({
await checkProjectCommitReadPermission({
actor,
actorId,
actorAuthMethod,
@@ -1063,7 +1168,7 @@ export const folderCommitServiceFactory = ({
environment: string;
path: string;
}) => {
await checkProjectPermission({
await checkProjectCommitReadPermission({
actor,
actorId,
actorAuthMethod,
@@ -1099,7 +1204,7 @@ export const folderCommitServiceFactory = ({
projectId: string;
commitId: string;
}) => {
await checkProjectPermission({
await checkProjectCommitReadPermission({
actor,
actorId,
actorAuthMethod,
@@ -1382,7 +1487,8 @@ export const folderCommitServiceFactory = ({
envId: string,
actorId: string,
actorType: ActorType,
projectId: string
projectId: string,
message?: string
) => {
await folderCommitDAL.transaction(async (tx) => {
const targetCommit = await folderCommitDAL.findById(targetCommitId, tx);
@@ -1480,7 +1586,7 @@ export const folderCommitServiceFactory = ({
actorInfo: {
actorType,
actorId,
message: "Deep rollback"
message: message || "Deep rollback"
},
folderId: folder.id,
projectId,
@@ -1508,7 +1614,7 @@ export const folderCommitServiceFactory = ({
actorOrgId: string;
projectId: string;
}) => {
await checkProjectPermission({
await checkProjectCommitReadPermission({
actor,
actorId,
actorAuthMethod,
@@ -1555,7 +1661,7 @@ export const folderCommitServiceFactory = ({
ProjectPermissionSub.Commits
);
// Check permissions first
await checkProjectPermission({
await checkProjectCommitReadPermission({
actor,
actorId,
projectId,
@@ -1569,32 +1675,12 @@ export const folderCommitServiceFactory = ({
throw new NotFoundError({ message: `Commit with ID ${commitId} not found` });
}
// Get all commits for this folder
const allCommits = await folderCommitDAL.findByFolderId(commitToRevert.folderId);
if (!allCommits || allCommits.length === 0) {
throw new NotFoundError({ message: `No commits found for folder ${commitToRevert.folderId}` });
}
const previousCommit = await folderCommitDAL.findCommitBefore(commitToRevert.folderId, commitToRevert.commitId);
// Sort commits by commitId (which appears to be numeric)
const sortedCommits = allCommits.sort((a, b) => {
if (a.commitId < b.commitId) return -1;
if (a.commitId > b.commitId) return 1;
return 0;
});
// Find the index of the commit to revert
const commitIndex = sortedCommits.findIndex((c) => c.id === commitId);
if (commitIndex === -1) {
throw new NotFoundError({ message: `Commit ${commitId} not found in the commit history` });
}
// If it's the first commit, we can't revert it (nothing before it)
if (commitIndex === 0) {
if (!previousCommit) {
throw new BadRequestError({ message: "Cannot revert the first commit" });
}
// Get the commit just before the one we want to revert
const previousCommit = sortedCommits[commitIndex - 1];
// Calculate the changes needed to go from current commit back to the previous one
const inverseChanges = await compareFolderStates({
currentCommitId: commitToRevert.id,