Skip to content

Commit

Permalink
misc: added API endpoint for fetching project ID
Browse files Browse the repository at this point in the history
  • Loading branch information
sheensantoscapadngan committed Aug 8, 2024
1 parent 14c1b4f commit 44bc09c
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 1 deletion.
4 changes: 4 additions & 0 deletions backend/src/lib/api-docs/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,10 @@ export const PROJECTS = {
GET: {
workspaceId: "The ID of the project."
},
GET_ID: {
slug: "The slug of the project to get",
name: "The name of the project to get"
},
UPDATE: {
workspaceId: "The ID of the project to update.",
name: "The new name of the project.",
Expand Down
49 changes: 49 additions & 0 deletions backend/src/server/routes/v1/project-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { z } from "zod";

import { IntegrationsSchema, ProjectMembershipsSchema, UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas";
import { PROJECTS } from "@app/lib/api-docs";
import { BadRequestError } from "@app/lib/errors";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
Expand Down Expand Up @@ -511,4 +512,52 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
return { serviceTokenData };
}
});

server.route({
method: "GET",
url: "/id",
config: {
rateLimit: readLimit
},
schema: {
querystring: z.object({
slug: z.string().trim().optional().describe(PROJECTS.GET_ID.slug),
name: z.string().trim().optional().describe(PROJECTS.GET_ID.name)
}),
response: {
200: z.object({
id: z.string()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
if (!req.query.slug && !req.query.name) {
throw new BadRequestError({
name: "Project name or slug is required."
});
}

const workspace = await server.services.project.getAProject({
filter: {
...(req.query.slug
? {
type: ProjectFilterType.SLUG,
slug: req.query.slug
}
: {
type: ProjectFilterType.NAME,
name: req.query.name as string
}),
orgId: req.permission.orgId
},
actorAuthMethod: req.permission.authMethod,
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId
});

return { id: workspace.id };
}
});
};
60 changes: 60 additions & 0 deletions backend/src/services/project/project-dal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,55 @@ export const projectDALFactory = (db: TDbClient) => {
}
};

const findProjectByName = async (name: string, orgId: string | undefined) => {
try {
if (!orgId) {
throw new BadRequestError({ message: "Organization ID is required when querying with slugs" });
}

const projects = await db
.replicaNode()(TableName.Project)
.where(`${TableName.Project}.name`, name)
.where(`${TableName.Project}.orgId`, orgId)
.leftJoin(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`)
.select(
selectAllTableCols(TableName.Project),
db.ref("id").withSchema(TableName.Environment).as("envId"),
db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
db.ref("name").withSchema(TableName.Environment).as("envName")
)
.orderBy([
{ column: `${TableName.Project}.name`, order: "asc" },
{ column: `${TableName.Environment}.position`, order: "asc" }
]);

const project = sqlNestRelationships({
data: projects,
key: "id",
parentMapper: ({ ...el }) => ({ _id: el.id, ...ProjectsSchema.parse(el) }),
childrenMapper: [
{
key: "envId",
label: "environments" as const,
mapper: ({ envId, envSlug, envName }) => ({
id: envId,
slug: envSlug,
name: envName
})
}
]
})?.[0];

if (!project) {
throw new BadRequestError({ message: "Project not found" });
}

return project;
} catch (error) {
throw new DatabaseError({ error, name: "Find project by slug" });
}
};

const findProjectByFilter = async (filter: Filter) => {
try {
if (filter.type === ProjectFilterType.ID) {
Expand All @@ -258,6 +307,17 @@ export const projectDALFactory = (db: TDbClient) => {

return await findProjectBySlug(filter.slug, filter.orgId);
}

if (filter.type === ProjectFilterType.NAME) {
if (!filter.orgId) {
throw new BadRequestError({
message: "Organization ID is required when querying with names"
});
}

return await findProjectByName(filter.name, filter.orgId);
}

throw new BadRequestError({ message: "Invalid filter type" });
} catch (error) {
if (error instanceof BadRequestError) {
Expand Down
8 changes: 7 additions & 1 deletion backend/src/services/project/project-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { KmsType } from "../kms/kms-types";

export enum ProjectFilterType {
ID = "id",
SLUG = "slug"
SLUG = "slug",
NAME = "name"
}

export type Filter =
Expand All @@ -19,6 +20,11 @@ export type Filter =
type: ProjectFilterType.SLUG;
slug: string;
orgId: string | undefined;
}
| {
type: ProjectFilterType.NAME;
name: string;
orgId: string | undefined;
};

export type TCreateProjectDTO = {
Expand Down
4 changes: 4 additions & 0 deletions docs/api-reference/endpoints/workspaces/get-workspace-id.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
title: "Get Project ID"
openapi: "GET /api/v1/workspace/id"
---
1 change: 1 addition & 0 deletions docs/mint.json
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,7 @@
"api-reference/endpoints/workspaces/create-workspace",
"api-reference/endpoints/workspaces/delete-workspace",
"api-reference/endpoints/workspaces/get-workspace",
"api-reference/endpoints/workspaces/get-workspace-id",
"api-reference/endpoints/workspaces/update-workspace",
"api-reference/endpoints/workspaces/secret-snapshots",
"api-reference/endpoints/workspaces/rollback-snapshot"
Expand Down

0 comments on commit 44bc09c

Please sign in to comment.