Skip to content
Open
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
8 changes: 2 additions & 6 deletions db/migrations/20240412003023_add_invites_table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export async function up(knex: Knex): Promise<void> {
table.increments('id').primary();
table.integer('workspaceId').unsigned();
table.integer('senderId').unsigned();
table.integer('inviteeId').unsigned();
table.string('inviteeEmail').notNullable();

table.timestamp('expiresAt').notNullable();
table
Expand All @@ -33,11 +33,7 @@ export async function up(knex: Knex): Promise<void> {
.references('id')
.inTable('workspaces')
.onDelete('CASCADE');
table
.foreign('inviteeId')
.references('id')
.inTable('users')
.onDelete('CASCADE');

table
.foreign('senderId')
.references('id')
Expand Down
14 changes: 7 additions & 7 deletions db/seeds/08-inviteSeeding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ export async function seed(knex: Knex): Promise<void> {
* */

await Promise.all([
EntityFactory.createInvite(1, 1, 2, 1, 'accepted'),
EntityFactory.createInvite(2, 1, 3, 1, 'accepted'),
EntityFactory.createInvite(3, 1, 4, 1, 'accepted'),
EntityFactory.createInvite(4, 2, 5, 3, 'cancelled'),
EntityFactory.createInvite(5, 6, 1, 3, 'pending'),
EntityFactory.createInvite(6, 3, 1, 4, 'pending'),
EntityFactory.createInvite(7, 5, 1, 5, 'cancelled'),
EntityFactory.createInvite(1, 1, 'email2@gmail.com', 1, 'accepted'),
EntityFactory.createInvite(2, 1, 'email3@gmail.com', 1, 'accepted'),
EntityFactory.createInvite(3, 1, 'email4@gmail.com', 1, 'accepted'),
EntityFactory.createInvite(4, 2, 'email5@gmail.com', 3, 'cancelled'),
EntityFactory.createInvite(5, 6, 'email1@gmail.com', 3, 'pending'),
EntityFactory.createInvite(6, 3, 'email1@gmail.com', 4, 'pending'),
EntityFactory.createInvite(7, 5, 'email1@gmail.com', 5, 'cancelled'),
]);
}
2 changes: 1 addition & 1 deletion src/api-docs/openAPIDocumentGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { filesRegistry } from '@/api/files/filesRoutes';
import { healthCheckRegistry } from '@/api/healthCheck/healthCheckRouter';
import { messageRegistery } from '@/api/messages/messageApi';
import { notificationsRegistry } from '@/api/notifications/notificationsRoutes';
import { reactionsRegistry } from '@/api/reactions/reactionsRouter';
import { reactionsRegistry } from '@/api/reactions/reactionApi';
import { userRegistry } from '@/api/user/userApi';
import { workspaceRegistry } from '@/api/workspace/workspaceApi';

Expand Down
47 changes: 0 additions & 47 deletions src/api/invites/__tests__/InviteRepository.test.ts

This file was deleted.

105 changes: 105 additions & 0 deletions src/api/invites/inviteApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { OpenAPIRegistry } from '@asteasolutions/zod-to-openapi';

import { createApiResponse } from '@/api-docs/openAPIResponseBuilders';
import { messageResponse } from '@/common/utils/commonResponses';

import {
CreateInviteSchema,
DeleteInviteSchema,
GetInviteSchema,
InviteSchema,
UpdateInviteSchema,
} from './invitesModel';

export const inviteRegistery = new OpenAPIRegistry();

const bearerAuth = inviteRegistery.registerComponent(
'securitySchemes',
'bearerAuth',
{
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
}
);

inviteRegistery.register('Invite', InviteSchema);

inviteRegistery.registerPath({
method: 'post',
path: '/invites',
tags: ['Invite'],
security: [{ [bearerAuth.name]: [] }],
request: {
body: {
content: {
'application/json': {
schema: CreateInviteSchema.shape.body,
},
},
},
},
responses: createApiResponse(InviteSchema, 'Success'),
});

inviteRegistery.registerPath({
method: 'get',
path: '/invites/{id}',
tags: ['Invite'],
security: [{ [bearerAuth.name]: [] }],
request: {
params: GetInviteSchema.shape.params,
},
responses: createApiResponse(InviteSchema, 'Success'),
});

inviteRegistery.registerPath({
method: 'patch',
path: '/invites/{id}',
tags: ['Invite'],
security: [{ [bearerAuth.name]: [] }],
request: {
body: {
content: {
'application/json': {
schema: UpdateInviteSchema.shape.body,
},
},
},
params: UpdateInviteSchema.shape.params,
},
responses: createApiResponse(messageResponse, 'Success'),
});

inviteRegistery.registerPath({
method: 'patch',
path: '/invites/{id}/acceptInvite',
tags: ['Invite'],
security: [{ [bearerAuth.name]: [] }],
request: {
params: GetInviteSchema.shape.params,
},
responses: createApiResponse(messageResponse, 'Success'),
});

inviteRegistery.registerPath({
method: 'patch',
path: '/invites/{id}/cancelInvite',
tags: ['Invite'],
security: [{ [bearerAuth.name]: [] }],
request: {
params: GetInviteSchema.shape.params,
},
responses: createApiResponse(messageResponse, 'Success'),
});

inviteRegistery.registerPath({
method: 'delete',
path: '/invites/{id}',
tags: ['Invite'],
security: [{ [bearerAuth.name]: [] }],
request: {
params: DeleteInviteSchema.shape.params,
},
responses: createApiResponse(messageResponse, 'Success'),
});
45 changes: 45 additions & 0 deletions src/api/invites/inviteRouter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import express, { Router } from 'express';

import { validateRequest } from '@/common/utils/httpHandlers';

import AuthController from '../auth/authController';
import InvitesController from './invitesController';
import { CreateInviteSchema } from './invitesModel';

export const inviteRouter: Router = (() => {
const router = express.Router();

router.post(
'/',
[AuthController.authenticate, validateRequest(CreateInviteSchema)],
InvitesController.createInvite
);

router.get(
'/:id',
[AuthController.authenticate],
InvitesController.getInviteById
);
router.patch(
'/:id',
[AuthController.authenticate],
InvitesController.updateInvite
);
router.patch(
'/:id/acceptInvite',
[AuthController.authenticate],
InvitesController.acceptInvite
);
router.patch(
'/:id/cancelInvite',
[AuthController.authenticate],
InvitesController.cancelInvite
);
router.delete(
'/:id',
[AuthController.authenticate],
InvitesController.deleteInvite
);

return router;
})();
37 changes: 37 additions & 0 deletions src/api/invites/inviteService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Knex } from 'knex';

import { CreateInviteDto } from './invitesModel';
import invitesRepository from './invitesRepository';

export function sendInvite(invite: CreateInviteDto, trx: Knex.Transaction) {
return invitesRepository.createInvite(invite, trx);
}

export function getInviteById(id: string, trx: Knex.Transaction) {
return invitesRepository.getInviteById(id, trx);
}

export function getInviteByWorkspaceId(
workspaceId: string,
trx: Knex.Transaction
) {
return invitesRepository.getInviteByWorkspaceId(workspaceId, trx);
}
export function updateInvite(
id: string,
status: string,
trx: Knex.Transaction
) {
return invitesRepository.updateInvite(id, status, trx);
}
export function acceptInvite(id: string, trx: Knex.Transaction) {
return invitesRepository.acceptInvite(id, trx);
}

export function cancelInvite(id: string, trx: Knex.Transaction) {
return invitesRepository.cancelInvite(id, trx);
}

export function deleteInvite(id: string, trx: Knex.Transaction) {
return invitesRepository.deleteInvite(id, trx);
}
65 changes: 42 additions & 23 deletions src/api/invites/invitesController.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import db from 'db/db';
import { Request, Response } from 'express';

import {
asyncHandler,
handleServiceResponse,
} from '@/common/utils/httpHandlers';

import { CreateInvite } from './invitesModel';
import invitesRepository from './invitesRepository';

const InvitesController = {
createInvite: async (req: Request, res: Response) => {
createInvite: asyncHandler(async (req: Request, res: Response) => {
const { inviteeEmail, workspaceId } = req.body;

const createInvitePayload: CreateInvite = {
Expand All @@ -17,35 +21,50 @@ const InvitesController = {
};

const invite = await invitesRepository.createInvite(
db,
createInvitePayload
createInvitePayload,
res.trx
);
res.json(invite);
},
getInviteById: async (req: Request, res: Response) => {
handleServiceResponse(res, invite, 'ok');
}),
getInviteById: asyncHandler(async (req: Request, res: Response) => {
const id = req.params.id;
const invite = await invitesRepository.getInviteById(db, id);
res.json(invite);
},
const invite = await invitesRepository.getInviteById(id, res.trx);
handleServiceResponse(res, invite, 'ok');
}),

getWorkspaceInvites: async (req: Request, res: Response) => {
getWorkspaceInvites: asyncHandler(async (req: Request, res: Response) => {
const workspaceId = req.params.id;
const invites = await invitesRepository.getInviteByWorkspaceId(
db,
workspaceId
workspaceId,
res.trx
);
res.json(invites);
},
acceptInvite: async (req: Request, res: Response) => {
handleServiceResponse(res, invites, 'ok');
}),
updateInvite: asyncHandler(async (req: Request, res: Response) => {
const id = req.params.id;
const { status } = req.body;
const updatedInvite = await invitesRepository.updateInvite(
id,
status,
res.trx
);
handleServiceResponse(res, updatedInvite, 'ok');
}),
acceptInvite: asyncHandler(async (req: Request, res: Response) => {
const id = req.params.id;
await invitesRepository.acceptInvite(id, res.trx);
handleServiceResponse(res, 'Invite accepted', 'ok');
}),
cancelInvite: asyncHandler(async (req: Request, res: Response) => {
const id = req.params.id;
await invitesRepository.acceptInvite(db, id);
res.sendStatus(200);
},
cancelInvite: async (req: Request, res: Response) => {
await invitesRepository.cancelInvite(id, res.trx);
handleServiceResponse(res, 'Invite cancelled', 'ok');
}),
deleteInvite: asyncHandler(async (req: Request, res: Response) => {
const id = req.params.id;
await invitesRepository.cancelInvite(db, id);
res.sendStatus(200);
},
await invitesRepository.deleteInvite(id, res.trx);
handleServiceResponse(res, 'Invite deleted', 'ok');
}),
};

export default InvitesController;
2 changes: 1 addition & 1 deletion src/api/invites/invitesModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ export type CreateInviteDto = Omit<Invite, 'id' | 'createdAt' | 'updatedAt'>;
export const CreateInviteSchema = z.object({
body: z.object({
inviteeEmail: z.string().email(),
workspaceId: z.number(),
}),
params: z.object({ id: z.number() }),
});
export const GetInviteSchema = z.object({
params: z.object({ id: z.number() }),
Expand Down
Loading