mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 09:30:09 +00:00
PIT: general improvements and fixes
This commit is contained in:
@@ -474,6 +474,24 @@ export const folderCommitDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
const findCommitBefore = async (
|
||||
folderId: string,
|
||||
commitId: bigint,
|
||||
tx?: Knex
|
||||
): Promise<TFolderCommits | undefined> => {
|
||||
try {
|
||||
const doc = await (tx || db.replicaNode())(TableName.FolderCommit)
|
||||
.where({ folderId })
|
||||
.where("commitId", "<", commitId.toString())
|
||||
.select(selectAllTableCols(TableName.FolderCommit))
|
||||
.orderBy("commitId", "desc")
|
||||
.first();
|
||||
return doc;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "FindCommitBefore" });
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
...restOfOrm,
|
||||
findByFolderId,
|
||||
@@ -489,6 +507,7 @@ export const folderCommitDALFactory = (db: TDbClient) => {
|
||||
findAllFolderCommitsAfter,
|
||||
findPreviousCommitTo,
|
||||
findById,
|
||||
findByFolderIdPaginated
|
||||
findByFolderIdPaginated,
|
||||
findCommitBefore
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
import { z } from "zod";
|
||||
|
||||
// Base schema shared by both secret and folder changes
|
||||
const baseChangeSchema = z.object({
|
||||
id: z.string(),
|
||||
folderCommitId: z.string(),
|
||||
changeType: z.string(),
|
||||
isUpdate: z.boolean().optional(),
|
||||
createdAt: z.union([z.string(), z.date()]),
|
||||
updatedAt: z.union([z.string(), z.date()]),
|
||||
actorMetadata: z
|
||||
.union([
|
||||
z.object({
|
||||
id: z.string().optional(),
|
||||
name: z.string().optional()
|
||||
}),
|
||||
z.unknown()
|
||||
])
|
||||
.optional(),
|
||||
actorType: z.string(),
|
||||
message: z.string().nullable().optional(),
|
||||
folderId: z.string()
|
||||
});
|
||||
|
||||
// Secret-specific versions schema
|
||||
const secretVersionSchema = z.object({
|
||||
secretKey: z.string(),
|
||||
secretComment: z.string(),
|
||||
skipMultilineEncoding: z.boolean().nullable().optional(),
|
||||
secretReminderRepeatDays: z.number().nullable().optional(),
|
||||
tags: z.array(z.string()).nullable().optional(),
|
||||
metadata: z.unknown().nullable().optional(),
|
||||
secretReminderNote: z.string().nullable().optional(),
|
||||
secretValue: z.string()
|
||||
});
|
||||
|
||||
// Folder-specific versions schema
|
||||
const folderVersionSchema = z.object({
|
||||
name: z.string().optional()
|
||||
});
|
||||
|
||||
// Secret commit change schema
|
||||
const secretCommitChangeSchema = baseChangeSchema.extend({
|
||||
resourceType: z.literal("secret"),
|
||||
secretVersionId: z.string().optional().nullable(),
|
||||
secretKey: z.string(),
|
||||
secretVersion: z.union([z.string(), z.number()]),
|
||||
secretId: z.string(),
|
||||
versions: z.array(secretVersionSchema).optional()
|
||||
});
|
||||
|
||||
// Folder commit change schema
|
||||
const folderCommitChangeSchema = baseChangeSchema.extend({
|
||||
resourceType: z.literal("folder"),
|
||||
folderVersionId: z.string().optional().nullable(),
|
||||
folderName: z.string(),
|
||||
folderChangeId: z.string(),
|
||||
folderVersion: z.union([z.string(), z.number()]),
|
||||
versions: z.array(folderVersionSchema).optional()
|
||||
});
|
||||
|
||||
// Discriminated union for commit changes
|
||||
export const commitChangeSchema = z.discriminatedUnion("resourceType", [
|
||||
secretCommitChangeSchema,
|
||||
folderCommitChangeSchema
|
||||
]);
|
||||
|
||||
// Commit schema
|
||||
const commitSchema = z.object({
|
||||
id: z.string(),
|
||||
commitId: z.string(),
|
||||
actorMetadata: z
|
||||
.union([
|
||||
z.object({
|
||||
id: z.string().optional(),
|
||||
name: z.string().optional()
|
||||
}),
|
||||
z.unknown()
|
||||
])
|
||||
.optional(),
|
||||
actorType: z.string(),
|
||||
message: z.string().nullable().optional(),
|
||||
folderId: z.string(),
|
||||
envId: z.string(),
|
||||
createdAt: z.union([z.string(), z.date()]),
|
||||
updatedAt: z.union([z.string(), z.date()]),
|
||||
changes: z.array(commitChangeSchema).optional()
|
||||
});
|
||||
|
||||
// Response schema
|
||||
export const commitChangesResponseSchema = z.object({
|
||||
changes: commitSchema
|
||||
});
|
||||
|
||||
// Base resource change schema for comparison results
|
||||
const baseResourceChangeSchema = z.object({
|
||||
id: z.string(),
|
||||
versionId: z.string(),
|
||||
oldVersionId: z.string().optional(),
|
||||
changeType: z.enum(["add", "delete", "update", "create"]),
|
||||
commitId: z.union([z.string(), z.bigint()]),
|
||||
createdAt: z.union([z.string(), z.date()]).optional(),
|
||||
parentId: z.string().optional(),
|
||||
isUpdate: z.boolean().optional(),
|
||||
fromVersion: z.union([z.string(), z.number()]).optional()
|
||||
});
|
||||
|
||||
// Secret resource change schema
|
||||
const secretResourceChangeSchema = baseResourceChangeSchema.extend({
|
||||
type: z.literal("secret"),
|
||||
secretKey: z.string().optional(),
|
||||
secretVersion: z.union([z.string(), z.number()]).optional(),
|
||||
secretId: z.string().optional(),
|
||||
versions: z
|
||||
.array(
|
||||
z.object({
|
||||
secretKey: z.string().optional(),
|
||||
secretComment: z.string().optional(),
|
||||
skipMultilineEncoding: z.boolean().nullable().optional(),
|
||||
secretReminderRepeatDays: z.number().nullable().optional(),
|
||||
tags: z.array(z.string()).nullable().optional(),
|
||||
metadata: z.unknown().nullable().optional(),
|
||||
secretReminderNote: z.string().nullable().optional(),
|
||||
secretValue: z.string().optional()
|
||||
})
|
||||
)
|
||||
.optional()
|
||||
});
|
||||
|
||||
// Folder resource change schema
|
||||
const folderResourceChangeSchema = baseResourceChangeSchema.extend({
|
||||
type: z.literal("folder"),
|
||||
folderName: z.string().optional(),
|
||||
folderVersion: z.union([z.string(), z.number()]).optional(),
|
||||
versions: z.array(folderVersionSchema).optional()
|
||||
});
|
||||
|
||||
// Discriminated union for resource changes
|
||||
export const resourceChangeSchema = z.discriminatedUnion("type", [
|
||||
secretResourceChangeSchema,
|
||||
folderResourceChangeSchema
|
||||
]);
|
||||
@@ -1,3 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
/* eslint-disable @typescript-eslint/return-await */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||
import { Knex } from "knex";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
@@ -9,6 +12,7 @@ import {
|
||||
ChangeType,
|
||||
CommitType,
|
||||
folderCommitServiceFactory,
|
||||
ResourceType,
|
||||
TFolderCommitServiceFactory
|
||||
} from "./folder-commit-service";
|
||||
|
||||
@@ -518,8 +522,20 @@ describe("folderCommitServiceFactory", () => {
|
||||
const actorType = ActorType.USER;
|
||||
|
||||
const differences = [
|
||||
{ type: "secret", id: "secret-1", versionId: "v1", changeType: ChangeType.CREATE, commitId: BigInt(1) },
|
||||
{ type: "folder", id: "folder-1", versionId: "v2", changeType: ChangeType.UPDATE, commitId: BigInt(1) }
|
||||
{
|
||||
type: ResourceType.SECRET,
|
||||
id: "secret-1",
|
||||
versionId: "v1",
|
||||
changeType: ChangeType.CREATE,
|
||||
commitId: BigInt(1)
|
||||
},
|
||||
{
|
||||
type: ResourceType.FOLDER,
|
||||
id: "folder-1",
|
||||
versionId: "v2",
|
||||
changeType: ChangeType.UPDATE,
|
||||
commitId: BigInt(1)
|
||||
}
|
||||
];
|
||||
|
||||
const secretVersions = {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
const baseChangeSchema = z.object({
|
||||
id: z.string(),
|
||||
folderCommitId: z.string(),
|
||||
changeType: z.string(),
|
||||
isUpdate: z.boolean().optional(),
|
||||
createdAt: z.union([z.string(), z.date()]),
|
||||
updatedAt: z.union([z.string(), z.date()]),
|
||||
actorMetadata: z
|
||||
.union([
|
||||
z.object({
|
||||
id: z.string().optional(),
|
||||
name: z.string().optional()
|
||||
}),
|
||||
z.unknown()
|
||||
])
|
||||
.optional(),
|
||||
actorType: z.string().optional(),
|
||||
message: z.string().optional().nullable(),
|
||||
folderId: z.string().optional()
|
||||
});
|
||||
|
||||
const commitChangeSchema = baseChangeSchema.extend({
|
||||
secretVersionId: z.string().optional().nullable(),
|
||||
folderVersionId: z.string().optional().nullable(),
|
||||
folderName: z.string().optional().nullable(),
|
||||
folderChangeId: z.string().optional().nullable(),
|
||||
secretKey: z.string().optional().nullable(),
|
||||
secretVersion: z.union([z.string(), z.number()]).optional().nullable(),
|
||||
secretId: z.string().optional().nullable(),
|
||||
folderVersion: z.union([z.string(), z.number()]).optional().nullable(),
|
||||
versions: z
|
||||
.array(
|
||||
z.object({
|
||||
secretKey: z.string().optional().nullable(),
|
||||
secretComment: z.string().optional().nullable(),
|
||||
skipMultilineEncoding: z.boolean().optional().nullable(),
|
||||
secretReminderRepeatDays: z.number().optional().nullable(),
|
||||
secretReminderNote: z.string().optional().nullable(),
|
||||
metadata: z.unknown().optional().nullable(),
|
||||
tags: z.array(z.string()).optional().nullable(),
|
||||
secretReminderRecipients: z.array(z.any()).optional().nullable(),
|
||||
secretValue: z.string().optional().nullable(),
|
||||
name: z.string().optional().nullable()
|
||||
})
|
||||
)
|
||||
.optional()
|
||||
});
|
||||
|
||||
const commitSchema = z.object({
|
||||
id: z.string(),
|
||||
commitId: z.string(),
|
||||
actorMetadata: z
|
||||
.union([
|
||||
z.object({
|
||||
id: z.string().optional(),
|
||||
name: z.string().optional()
|
||||
}),
|
||||
z.unknown()
|
||||
])
|
||||
.optional(),
|
||||
actorType: z.string(),
|
||||
message: z.string().nullable().optional(),
|
||||
folderId: z.string(),
|
||||
envId: z.string(),
|
||||
createdAt: z.union([z.string(), z.date()]),
|
||||
updatedAt: z.union([z.string(), z.date()]),
|
||||
changes: z.array(commitChangeSchema).optional()
|
||||
});
|
||||
|
||||
export const commitChangesResponseSchema = z.object({
|
||||
changes: commitSchema
|
||||
});
|
||||
Reference in New Issue
Block a user