diff --git a/backend/src/db/migrations/20240806185442_drop-tag-name.ts b/backend/src/db/migrations/20240806185442_drop-tag-name.ts new file mode 100644 index 000000000..db85ab216 --- /dev/null +++ b/backend/src/db/migrations/20240806185442_drop-tag-name.ts @@ -0,0 +1,21 @@ +import { Knex } from "knex"; + +import { TableName } from "../schemas"; + +export async function up(knex: Knex): Promise { + const hasNameField = await knex.schema.hasColumn(TableName.SecretTag, "name"); + if (hasNameField) { + await knex.schema.alterTable(TableName.SecretTag, (t) => { + t.dropColumn("name"); + }); + } +} + +export async function down(knex: Knex): Promise { + const hasNameField = await knex.schema.hasColumn(TableName.SecretTag, "name"); + if (!hasNameField) { + await knex.schema.alterTable(TableName.SecretTag, (t) => { + t.string("name"); + }); + } +} diff --git a/backend/src/db/schemas/secret-tags.ts b/backend/src/db/schemas/secret-tags.ts index 04bb7b752..7ba7204a4 100644 --- a/backend/src/db/schemas/secret-tags.ts +++ b/backend/src/db/schemas/secret-tags.ts @@ -9,7 +9,6 @@ import { TImmutableDBKeys } from "./models"; export const SecretTagsSchema = z.object({ id: z.string().uuid(), - name: z.string(), slug: z.string(), color: z.string().nullable().optional(), createdAt: z.date(), diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-secret-dal.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-secret-dal.ts index b827f6a21..0f24c75b3 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-secret-dal.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-secret-dal.ts @@ -124,9 +124,9 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => { { key: "tagJnId", label: "tags" as const, - mapper: ({ tagId: id, tagName: name, tagSlug: slug, tagColor: color }) => ({ + mapper: ({ tagId: id, tagSlug: slug, tagColor: color }) => ({ id, - name, + name: slug, slug, color }) @@ -292,9 +292,9 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => { { key: "tagJnId", label: "tags" as const, - mapper: ({ tagId: id, tagName: name, tagSlug: slug, tagColor: color }) => ({ + mapper: ({ tagId: id, tagSlug: slug, tagColor: color }) => ({ id, - name, + name: slug, slug, color }) diff --git a/backend/src/ee/services/secret-snapshot/snapshot-dal.ts b/backend/src/ee/services/secret-snapshot/snapshot-dal.ts index 8e5014fdd..cee2292ad 100644 --- a/backend/src/ee/services/secret-snapshot/snapshot-dal.ts +++ b/backend/src/ee/services/secret-snapshot/snapshot-dal.ts @@ -377,9 +377,9 @@ export const snapshotDALFactory = (db: TDbClient) => { { key: "tagVersionId", label: "tags" as const, - mapper: ({ tagId: id, tagName: name, tagSlug: slug, tagColor: color, tagVersionId: vId }) => ({ + mapper: ({ tagId: id, tagSlug: slug, tagColor: color, tagVersionId: vId }) => ({ id, - name, + name: slug, slug, color, vId @@ -532,9 +532,9 @@ export const snapshotDALFactory = (db: TDbClient) => { { key: "tagVersionId", label: "tags" as const, - mapper: ({ tagId: id, tagName: name, tagSlug: slug, tagColor: color, tagVersionId: vId }) => ({ + mapper: ({ tagId: id, tagSlug: slug, tagColor: color, tagVersionId: vId }) => ({ id, - name, + name: slug, slug, color, vId diff --git a/backend/src/server/routes/v1/secret-tag-router.ts b/backend/src/server/routes/v1/secret-tag-router.ts index ce92409f6..4786548c6 100644 --- a/backend/src/server/routes/v1/secret-tag-router.ts +++ b/backend/src/server/routes/v1/secret-tag-router.ts @@ -1,3 +1,4 @@ +import slugify from "@sindresorhus/slugify"; import { z } from "zod"; import { SecretTagsSchema } from "@app/db/schemas"; @@ -49,7 +50,8 @@ export const registerSecretTagRouter = async (server: FastifyZodProvider) => { }), response: { 200: z.object({ - workspaceTag: SecretTagsSchema + // akhilmhdh: for terraform backward compatiability + workspaceTag: SecretTagsSchema.extend({ name: z.string() }) }) } }, @@ -79,7 +81,7 @@ export const registerSecretTagRouter = async (server: FastifyZodProvider) => { }), response: { 200: z.object({ - workspaceTag: SecretTagsSchema + workspaceTag: SecretTagsSchema.extend({ name: z.string() }) }) } }, @@ -108,8 +110,14 @@ export const registerSecretTagRouter = async (server: FastifyZodProvider) => { projectId: z.string().trim().describe(SECRET_TAGS.CREATE.projectId) }), body: z.object({ - name: z.string().trim().describe(SECRET_TAGS.CREATE.name), - slug: z.string().trim().describe(SECRET_TAGS.CREATE.slug), + slug: z + .string() + .toLowerCase() + .trim() + .describe(SECRET_TAGS.CREATE.slug) + .refine((v) => slugify(v) === v, { + message: "Invalid slug. Should contain only characters, numbers and hyphen." + }), color: z.string().trim().describe(SECRET_TAGS.CREATE.color) }), response: { @@ -144,8 +152,14 @@ export const registerSecretTagRouter = async (server: FastifyZodProvider) => { tagId: z.string().trim().describe(SECRET_TAGS.UPDATE.tagId) }), body: z.object({ - name: z.string().trim().describe(SECRET_TAGS.UPDATE.name), - slug: z.string().trim().describe(SECRET_TAGS.UPDATE.slug), + slug: z + .string() + .toLowerCase() + .trim() + .describe(SECRET_TAGS.UPDATE.slug) + .refine((v) => slugify(v) === v, { + message: "Invalid slug. Should contain only characters, numbers and hyphen." + }), color: z.string().trim().describe(SECRET_TAGS.UPDATE.color) }), response: { diff --git a/backend/src/services/secret-tag/secret-tag-service.ts b/backend/src/services/secret-tag/secret-tag-service.ts index 76b57dc90..dd595f046 100644 --- a/backend/src/services/secret-tag/secret-tag-service.ts +++ b/backend/src/services/secret-tag/secret-tag-service.ts @@ -22,16 +22,7 @@ type TSecretTagServiceFactoryDep = { export type TSecretTagServiceFactory = ReturnType; export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSecretTagServiceFactoryDep) => { - const createTag = async ({ - name, - slug, - actor, - color, - actorId, - actorOrgId, - actorAuthMethod, - projectId - }: TCreateTagDTO) => { + const createTag = async ({ slug, actor, color, actorId, actorOrgId, actorAuthMethod, projectId }: TCreateTagDTO) => { const { permission } = await permissionService.getProjectPermission( actor, actorId, @@ -46,7 +37,6 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe const newTag = await secretTagDAL.create({ projectId, - name, slug, color, createdBy: actorId, @@ -55,7 +45,7 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe return newTag; }; - const updateTag = async ({ actorId, actor, actorOrgId, actorAuthMethod, id, name, color, slug }: TUpdateTagDTO) => { + const updateTag = async ({ actorId, actor, actorOrgId, actorAuthMethod, id, color, slug }: TUpdateTagDTO) => { const tag = await secretTagDAL.findById(id); if (!tag) throw new BadRequestError({ message: "Tag doesn't exist" }); @@ -73,7 +63,7 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe ); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Tags); - const updatedTag = await secretTagDAL.updateById(tag.id, { name, color, slug }); + const updatedTag = await secretTagDAL.updateById(tag.id, { color, slug }); return updatedTag; }; @@ -107,7 +97,7 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe ); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Tags); - return tag; + return { ...tag, name: tag.slug }; }; const getTagBySlug = async ({ actorId, actor, actorOrgId, actorAuthMethod, slug, projectId }: TGetTagBySlugDTO) => { @@ -123,7 +113,7 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe ); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Tags); - return tag; + return { ...tag, name: tag.slug }; }; const getProjectTags = async ({ actor, actorId, actorOrgId, actorAuthMethod, projectId }: TListProjectTagsDTO) => { diff --git a/backend/src/services/secret-tag/secret-tag-types.ts b/backend/src/services/secret-tag/secret-tag-types.ts index f2ace0901..ede0c55a3 100644 --- a/backend/src/services/secret-tag/secret-tag-types.ts +++ b/backend/src/services/secret-tag/secret-tag-types.ts @@ -1,14 +1,12 @@ import { TProjectPermission } from "@app/lib/types"; export type TCreateTagDTO = { - name: string; color: string; slug: string; } & TProjectPermission; export type TUpdateTagDTO = { id: string; - name?: string; slug?: string; color?: string; } & Omit; diff --git a/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts b/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts index b41e2c250..53ad29b17 100644 --- a/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts +++ b/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts @@ -136,7 +136,6 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => { .select(db.ref("id").withSchema(TableName.SecretTag).as("tagId")) .select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor")) .select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug")) - .select(db.ref("name").withSchema(TableName.SecretTag).as("tagName")) .orderBy("id", "asc"); const data = sqlNestRelationships({ @@ -147,11 +146,11 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => { { key: "tagId", label: "tags" as const, - mapper: ({ tagId: id, tagColor: color, tagSlug: slug, tagName: name }) => ({ + mapper: ({ tagId: id, tagColor: color, tagSlug: slug }) => ({ id, color, slug, - name + name: slug }) } ] @@ -169,14 +168,13 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => { .where({ [`${TableName.SecretV2}Id` as const]: secretId }) .select(db.ref("id").withSchema(TableName.SecretTag).as("tagId")) .select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor")) - .select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug")) - .select(db.ref("name").withSchema(TableName.SecretTag).as("tagName")); + .select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug")); return tags.map((el) => ({ id: el.tagId, color: el.tagColor, slug: el.tagSlug, - name: el.tagName + name: el.tagSlug })); } catch (error) { throw new DatabaseError({ error, name: "get secret tags" }); @@ -210,7 +208,6 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => { .select(db.ref("id").withSchema(TableName.SecretTag).as("tagId")) .select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor")) .select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug")) - .select(db.ref("name").withSchema(TableName.SecretTag).as("tagName")) .orderBy("id", "asc"); const data = sqlNestRelationships({ @@ -221,11 +218,11 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => { { key: "tagId", label: "tags" as const, - mapper: ({ tagId: id, tagColor: color, tagSlug: slug, tagName: name }) => ({ + mapper: ({ tagId: id, tagColor: color, tagSlug: slug }) => ({ id, color, slug, - name + name: slug }) } ] @@ -350,8 +347,7 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => { .select(selectAllTableCols(TableName.SecretV2)) .select(db.ref("id").withSchema(TableName.SecretTag).as("tagId")) .select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor")) - .select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug")) - .select(db.ref("name").withSchema(TableName.SecretTag).as("tagName")); + .select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug")); const docs = sqlNestRelationships({ data: rawDocs, key: "id", @@ -360,11 +356,11 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => { { key: "tagId", label: "tags" as const, - mapper: ({ tagId: id, tagColor: color, tagSlug: slug, tagName: name }) => ({ + mapper: ({ tagId: id, tagColor: color, tagSlug: slug }) => ({ id, color, slug, - name + name: slug }) } ] diff --git a/backend/src/services/secret/secret-dal.ts b/backend/src/services/secret/secret-dal.ts index aac01d393..c3d9eef73 100644 --- a/backend/src/services/secret/secret-dal.ts +++ b/backend/src/services/secret/secret-dal.ts @@ -123,7 +123,6 @@ export const secretDALFactory = (db: TDbClient) => { .select(db.ref("id").withSchema(TableName.SecretTag).as("tagId")) .select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor")) .select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug")) - .select(db.ref("name").withSchema(TableName.SecretTag).as("tagName")) .orderBy("id", "asc"); const data = sqlNestRelationships({ data: secs, @@ -133,11 +132,11 @@ export const secretDALFactory = (db: TDbClient) => { { key: "tagId", label: "tags" as const, - mapper: ({ tagId: id, tagColor: color, tagSlug: slug, tagName: name }) => ({ + mapper: ({ tagId: id, tagColor: color, tagSlug: slug }) => ({ id, color, slug, - name + name: slug }) } ] @@ -155,14 +154,13 @@ export const secretDALFactory = (db: TDbClient) => { .where({ [`${TableName.Secret}Id` as const]: secretId }) .select(db.ref("id").withSchema(TableName.SecretTag).as("tagId")) .select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor")) - .select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug")) - .select(db.ref("name").withSchema(TableName.SecretTag).as("tagName")); + .select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug")); return tags.map((el) => ({ id: el.tagId, color: el.tagColor, slug: el.tagSlug, - name: el.tagName + name: el.tagSlug })); } catch (error) { throw new DatabaseError({ error, name: "get secret tags" }); @@ -188,7 +186,6 @@ export const secretDALFactory = (db: TDbClient) => { .select(db.ref("id").withSchema(TableName.SecretTag).as("tagId")) .select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor")) .select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug")) - .select(db.ref("name").withSchema(TableName.SecretTag).as("tagName")) .orderBy("id", "asc"); const data = sqlNestRelationships({ data: secs, @@ -198,11 +195,11 @@ export const secretDALFactory = (db: TDbClient) => { { key: "tagId", label: "tags" as const, - mapper: ({ tagId: id, tagColor: color, tagSlug: slug, tagName: name }) => ({ + mapper: ({ tagId: id, tagColor: color, tagSlug: slug }) => ({ id, color, slug, - name + name: slug }) } ] @@ -328,11 +325,11 @@ export const secretDALFactory = (db: TDbClient) => { { key: "tagId", label: "tags" as const, - mapper: ({ tagId: id, tagColor: color, tagSlug: slug, tagName: name }) => ({ + mapper: ({ tagId: id, tagColor: color, tagSlug: slug }) => ({ id, color, slug, - name + name: slug }) } ] diff --git a/backend/src/services/secret/secret-fns.ts b/backend/src/services/secret/secret-fns.ts index 662067645..d41770846 100644 --- a/backend/src/services/secret/secret-fns.ts +++ b/backend/src/services/secret/secret-fns.ts @@ -370,7 +370,6 @@ export const decryptSecretRaw = ( id: string; slug: string; color?: string | null; - name: string; }[]; }, key: string @@ -412,7 +411,7 @@ export const decryptSecretRaw = ( _id: secret.id, id: secret.id, user: secret.userId, - tags: secret.tags, + tags: secret.tags?.map((el) => ({ ...el, name: el.slug })), skipMultilineEncoding: secret.skipMultilineEncoding, secretReminderRepeatDays: secret.secretReminderRepeatDays, secretReminderNote: secret.secretReminderNote, diff --git a/backend/src/services/secret/secret-service.ts b/backend/src/services/secret/secret-service.ts index 35c1de312..f338d1fda 100644 --- a/backend/src/services/secret/secret-service.ts +++ b/backend/src/services/secret/secret-service.ts @@ -2086,7 +2086,7 @@ export const secretServiceFactory = ({ return { ...updatedSecret[0], - tags: [...existingSecretTags, ...tags].map((t) => ({ id: t.id, slug: t.slug, name: t.name, color: t.color })) + tags: [...existingSecretTags, ...tags].map((t) => ({ id: t.id, slug: t.slug, color: t.color })) }; };