Skip to content

Commit

Permalink
add admin contract
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeh committed Sep 24, 2024
1 parent 5200a58 commit a491607
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 14 deletions.
20 changes: 14 additions & 6 deletions api/src/modules/admin/admin.controller.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
import { Body, Controller, Post, UseGuards } from '@nestjs/common';
import { Controller, UseGuards } from '@nestjs/common';
import { RolesGuard } from '@api/modules/auth/guards/roles.guard';
import { AuthenticationService } from '@api/modules/auth/authentication/authentication.service';
import { CreateUserDto } from '@shared/schemas/users/create-user.schema';
import { JwtAuthGuard } from '@api/modules/auth/guards/jwt-auth.guard';
import { ROLES } from '@api/modules/auth/authorisation/roles.enum';
import { RequiredRoles } from '@api/modules/auth/decorators/roles.decorator';
import { tsRestHandler, TsRestHandler } from '@ts-rest/nest';
import { ControllerResponse } from '@api/types/controller-response.type';
import { adminContract } from '@shared/contracts/admin.contract';

@Controller('admin')
@Controller()
@UseGuards(JwtAuthGuard, RolesGuard)
export class AdminController {
constructor(private readonly auth: AuthenticationService) {}

@RequiredRoles(ROLES.ADMIN)
@Post('/users')
async createUser(@Body() createUserDto: CreateUserDto): Promise<void> {
return this.auth.createUser(createUserDto);
@TsRestHandler(adminContract.createUser)
async createUser(): Promise<ControllerResponse> {
return tsRestHandler(adminContract.createUser, async ({ body }) => {
await this.auth.createUser(body);
return {
status: 201,
body: null,
};
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ import { LocalAuthGuard } from '@api/modules/auth/guards/local-auth.guard';
import { GetUser } from '@api/modules/auth/decorators/get-user.decorator';
import { Public } from '@api/modules/auth/decorators/is-public.decorator';
import { PasswordRecoveryService } from '@api/modules/auth/services/password-recovery.service';
import { authContract } from '@shared/contracts/auth/auth.contract';

import { tsRestHandler, TsRestHandler } from '@ts-rest/nest';
import { ControllerResponse } from '@api/types/controller-response.type';
import { AuthGuard } from '@nestjs/passport';
import { ResetPassword } from '@api/modules/auth/strategies/reset-password.strategy';
import { authContract } from '@shared/contracts/auth.contract';

@Controller()
@UseInterceptors(ClassSerializerInterceptor)
Expand Down
14 changes: 9 additions & 5 deletions api/test/integration/auth/create-user.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ describe('Create Users', () => {

beforeAll(async () => {
testManager = await TestManager.createTestManager();

mockEmailService =
testManager.getModule<MockEmailService>(IEmailServiceToken);
});
beforeEach(async () => {
const { user, jwtToken: token } = await testManager.setUpTestUser();
testUser = user;
jwtToken = token;
mockEmailService =
testManager.getModule<MockEmailService>(IEmailServiceToken);
});

afterEach(async () => {
Expand All @@ -34,7 +37,9 @@ describe('Create Users', () => {
// Given a user exists with valid credentials
// But the user has the role partner

const user = await testManager.mocks().createUser({ role: ROLES.PARTNER });
const user = await testManager
.mocks()
.createUser({ role: ROLES.PARTNER, email: '[email protected]' });
const { jwtToken } = await testManager.logUserIn(user);

// When the user creates a new user
Expand All @@ -57,7 +62,7 @@ describe('Create Users', () => {
.request()
.post('/admin/users')
.set('Authorization', `Bearer ${jwtToken}`)
.send({ email: testUser.email, password: '12345678' });
.send({ email: testUser.email, partnerName: 'test' });

// Then the user should receive a 409 status code
expect(response.status).toBe(HttpStatus.CONFLICT);
Expand All @@ -72,7 +77,6 @@ describe('Create Users', () => {
// beforeAll
const newUser = {
email: '[email protected]',
password: '12345678',
partnerName: 'test',
};

Expand Down
18 changes: 18 additions & 0 deletions shared/contracts/admin.contract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { initContract } from "@ts-rest/core";
import { JSONAPIError } from "@shared/dtos/json-api.error";
import { CreateUserSchema } from "@shared/schemas/users/create-user.schema";

// TODO: This is a scaffold. We need to define types for responses, zod schemas for body and query param validation etc.

const contract = initContract();
export const adminContract = contract.router({
createUser: {
method: "POST",
path: "/admin/users",
responses: {
201: contract.type<null>(),
401: contract.type<JSONAPIError>(),
},
body: CreateUserSchema,
},
});
File renamed without changes.
4 changes: 3 additions & 1 deletion shared/contracts/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { initContract } from "@ts-rest/core";
import { authContract } from "./auth/auth.contract";
import { adminContract } from "@shared/contracts/admin.contract";
import { authContract } from "@shared/contracts/auth.contract";

const contract = initContract();

export const router = contract.router({
auth: authContract,
admin: adminContract,
});
2 changes: 1 addition & 1 deletion shared/schemas/users/create-user.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { z } from "zod";

export const CreateUserSchema = z.object({
email: z.string().email(),
name: z.string(),
name: z.string().optional(),
partnerName: z.string(),
});

Expand Down

0 comments on commit a491607

Please sign in to comment.