Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(BE) Generate slug for workspace and document #78

Merged
merged 4 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 30 additions & 22 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
"passport-github": "^1.1.0",
"passport-jwt": "^4.0.1",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1"
"rxjs": "^7.8.1",
"slugify": "^1.6.6"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
Expand Down
2 changes: 2 additions & 0 deletions backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ model UserWorkspace {
model Workspace {
id String @id @default(auto()) @map("_id") @db.ObjectId
title String
slug String
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
documentList Document[]
Expand All @@ -50,6 +51,7 @@ model Document {
id String @id @default(auto()) @map("_id") @db.ObjectId
yorkieDocumentId String @map("yorkie_document_id")
title String
slug String
content String?
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
Expand Down
2 changes: 2 additions & 0 deletions backend/src/documents/types/document-domain.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export class DocumentDomain {
yorkieDocumentId: string;
@ApiProperty({ type: String, description: "Title of the document" })
title: string;
@ApiProperty({ type: String, description: "Slug of the document" })
slug: string;
@ApiProperty({ type: String, description: "Content of the document", required: false })
content?: string;
@ApiProperty({ type: Date, description: "Created date of the document" })
Expand Down
4 changes: 2 additions & 2 deletions backend/src/users/types/user-domain.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ export class UserDomain {
id: string;
@ApiProperty({ type: String, description: "Nickname of user" })
nickname: string;
@ApiProperty({ type: String, description: "Last worksace ID of user" })
lastWorkspaceId: string;
@ApiProperty({ type: String, description: "Last worksace slug of user" })
lastWorkspaceSlug: string;
@ApiProperty({ type: Date, description: "Created date of user" })
createdAt: Date;
@ApiProperty({ type: Date, description: "Updated date of user" })
Expand Down
25 changes: 22 additions & 3 deletions backend/src/users/users.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { User } from "@prisma/client";
import { PrismaService } from "src/db/prisma.service";
import { FindUserResponse } from "./types/find-user-response.type";
import { WorkspaceRoleConstants } from "src/utils/constants/auth-role";
import slugify from "slugify";

@Injectable()
export class UsersService {
Expand All @@ -11,7 +12,11 @@ export class UsersService {
async findOne(userId: string): Promise<FindUserResponse> {
const foundUserWorkspace = await this.prismaService.userWorkspace.findFirst({
select: {
workspaceId: true,
workspace: {
select: {
slug: true,
},
},
},
where: {
userId,
Expand All @@ -35,7 +40,7 @@ export class UsersService {

return {
...foundUser,
lastWorkspaceId: foundUserWorkspace.workspaceId,
lastWorkspaceSlug: foundUserWorkspace.workspace.slug,
};
}

Expand Down Expand Up @@ -63,9 +68,23 @@ export class UsersService {
},
});

const title = `${user.nickname}'s Workspace`;
let slug = slugify(title);

const duplicatedWorkspaceList = await this.prismaService.workspace.findMany({
where: {
slug,
},
});

if (duplicatedWorkspaceList.length) {
slug += `-${duplicatedWorkspaceList.length + 1}`;
}

const workspace = await this.prismaService.workspace.create({
data: {
title: `${user.nickname}'s Workspace`,
title,
slug,
},
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import { CreateWorkspaceDocumentShareTokenDto } from "./dto/create-workspace-doc
export class WorkspaceDocumentsController {
constructor(private workspaceDocumentsService: WorkspaceDocumentsService) {}

@Get(":document_id")
@Get(":document_slug")
@ApiOperation({
summary: "Retrieve a Document in the Workspace",
description: "If the user has the access permissions, return a document.",
Expand All @@ -50,9 +50,9 @@ export class WorkspaceDocumentsController {
async findOne(
@Req() req: AuthroizedRequest,
@Param("workspace_id") workspaceId: string,
@Param("document_id") documentId: string
@Param("document_slug") documentSlug: string
): Promise<FindWorkspaceDocumentResponse> {
return this.workspaceDocumentsService.findOne(req.user.id, workspaceId, documentId);
return this.workspaceDocumentsService.findOneBySlug(req.user.id, workspaceId, documentSlug);
}

@Get("")
Expand Down
20 changes: 17 additions & 3 deletions backend/src/workspace-documents/workspace-documents.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { FindWorkspaceDocumentsResponse } from "./types/find-workspace-documents
import { JwtService } from "@nestjs/jwt";
import { CreateWorkspaceDocumentShareTokenResponse } from "./types/create-workspace-document-share-token-response.type";
import { ShareRole } from "src/utils/types/share-role.type";
import slugify from "slugify";

@Injectable()
export class WorkspaceDocumentsService {
Expand All @@ -25,16 +26,29 @@ export class WorkspaceDocumentsService {
throw new NotFoundException();
}

let slug = slugify(title);

const duplicatedDocumentList = await this.prismaService.document.findMany({
where: {
slug,
},
});

if (duplicatedDocumentList.length) {
slug += `-${duplicatedDocumentList.length + 1}`;
}

return this.prismaService.document.create({
data: {
title,
slug,
workspaceId,
yorkieDocumentId: Math.random().toString(36).substring(7),
},
});
}

async findOne(userId: string, workspaceId: string, documentId: string) {
async findOneBySlug(userId: string, workspaceId: string, documentSlug: string) {
try {
await this.prismaService.userWorkspace.findFirstOrThrow({
where: {
Expand All @@ -43,9 +57,9 @@ export class WorkspaceDocumentsService {
},
});

return this.prismaService.document.findUniqueOrThrow({
return this.prismaService.document.findFirstOrThrow({
where: {
id: documentId,
slug: documentSlug,
},
});
} catch (e) {
Expand Down
2 changes: 2 additions & 0 deletions backend/src/workspaces/types/workspace-domain.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export class WorkspaceDomain {
id: string;
@ApiProperty({ type: String, description: "Title of the workspace" })
title: string;
@ApiProperty({ type: String, description: "Slug of the workspace" })
slug: string;
@ApiProperty({ type: Date, description: "Created date of the workspace" })
createdAt: Date;
@ApiProperty({ type: Date, description: "Updated date of the workspace" })
Expand Down
6 changes: 3 additions & 3 deletions backend/src/workspaces/workspaces.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export class WorkspacesController {
return this.workspacesService.create(req.user.id, createWorkspaceDto.title);
}

@Get(":id")
@Get(":workspace_slug")
@ApiOperation({
summary: "Retrieve a Workspace",
description: "If the user has the access permissions, return a workspace.",
Expand All @@ -65,9 +65,9 @@ export class WorkspacesController {
})
async findOne(
@Req() req: AuthroizedRequest,
@Param("id") workspaceId: string
@Param("workspace_slug") workspaceSlug: string
): Promise<FindWorkspaceResponse> {
return this.workspacesService.findOne(req.user.id, workspaceId);
return this.workspacesService.findOneBySlug(req.user.id, workspaceSlug);
}

@Get("")
Expand Down
28 changes: 22 additions & 6 deletions backend/src/workspaces/workspaces.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { JwtService } from "@nestjs/jwt";
import { CreateInvitationTokenResponse } from "./types/create-inviation-token-response.type";
import { InvitationTokenPayload } from "./types/inviation-token-payload.type";
import { WorkspaceRoleConstants } from "src/utils/constants/auth-role";
import slugify from "slugify";

@Injectable()
export class WorkspacesService {
Expand All @@ -15,9 +16,22 @@ export class WorkspacesService {
) {}

async create(userId: string, title: string): Promise<Workspace> {
let slug = slugify(title);

const duplicatedWorkspaceList = await this.prismaService.workspace.findMany({
where: {
slug,
},
});

if (duplicatedWorkspaceList.length) {
slug += `-${duplicatedWorkspaceList.length + 1}`;
}

const workspace = await this.prismaService.workspace.create({
data: {
title,
slug,
},
});

Expand All @@ -32,20 +46,22 @@ export class WorkspacesService {
return workspace;
}

async findOne(userId: string, workspaceId: string) {
async findOneBySlug(userId: string, workspaceSlug: string) {
try {
await this.prismaService.userWorkspace.findFirstOrThrow({
const foundWorkspace = await this.prismaService.workspace.findFirstOrThrow({
where: {
userId,
workspaceId,
slug: workspaceSlug,
},
});

return this.prismaService.workspace.findUniqueOrThrow({
await this.prismaService.userWorkspace.findFirstOrThrow({
where: {
id: workspaceId,
userId,
workspaceId: foundWorkspace.id,
},
});

return foundWorkspace;
} catch (e) {
throw new NotFoundException();
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/common/GuestRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const GuestRoute = (props: RejectLoggedInRouteProps) => {
if (isLoggedIn) {
return (
<Navigate
to={`/workspace/${userStore.data?.lastWorkspaceId}`}
to={`/workspace/${userStore.data?.lastWorkspaceSlug}`}
state={{ from: location }}
replace
/>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/drawers/WorkspaceDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const DRAWER_WIDTH = 240;
function WorkspaceDrawer() {
const params = useParams();
const userStore = useSelector(selectUser);
const { data: workspace } = useGetWorkspaceQuery(params.workspaceId);
const { data: workspace } = useGetWorkspaceQuery(params.workspaceSlug);
const [profileAnchorEl, setProfileAnchorEl] = useState<(EventTarget & Element) | null>(null);
const [workspaceListAnchorEl, setWorkspaceListAnchorEl] = useState<
(EventTarget & Element) | null
Expand Down
Loading
Loading