Skip to content

Commit

Permalink
feat(api): Add getCustomProject and getCustomProjects contracts and e…
Browse files Browse the repository at this point in the history
…ndpoints
  • Loading branch information
alepefe committed Dec 5, 2024
1 parent 7e47e50 commit 2772c30
Show file tree
Hide file tree
Showing 8 changed files with 942 additions and 6 deletions.
40 changes: 39 additions & 1 deletion api/src/modules/custom-projects/custom-projects.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export class CustomProjectsController {
): Promise<ControllerResponse> {
return tsRestHandler(
customProjectContract.createCustomProject,
async ({ body }) => {
async () => {
const customProject = await this.customProjects.create(dto as any);
return {
status: 201,
Expand All @@ -93,4 +93,42 @@ export class CustomProjectsController {
},
);
}

@UseGuards(AuthGuard('jwt'), RolesGuard)
@RequiredRoles(ROLES.PARTNER, ROLES.ADMIN)
@TsRestHandler(customProjectContract.getCustomProject)
async getCustomProjectById(
@GetUser() user: User,
): Promise<ControllerResponse> {
return tsRestHandler(
customProjectContract.getCustomProject,
async ({ params: { id }, query }) => {
const data = await this.customProjects.getById(id, query, { user });
return { body: { data }, status: HttpStatus.OK };
},
);
}

@UseGuards(AuthGuard('jwt'), RolesGuard)
@RequiredRoles(ROLES.PARTNER, ROLES.ADMIN)
@TsRestHandler(customProjectContract.getCustomProjects)
async getCustomProjects(@GetUser() user: User): Promise<ControllerResponse> {
return tsRestHandler(
customProjectContract.getCustomProjects,
async ({ query }) => {
try {
const { data, metadata } = await this.customProjects.findAllPaginated(
query,
undefined,
{
user,
},
);
return { body: { data, metadata }, status: HttpStatus.OK };
} catch (e) {
console.error(e);
}
},
);
}
}
31 changes: 30 additions & 1 deletion api/src/modules/custom-projects/custom-projects.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
import { AppBaseService } from '@api/utils/app-base.service';
import { CreateCustomProjectDto } from '@api/modules/custom-projects/dto/create-custom-project-dto';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Repository, SelectQueryBuilder } from 'typeorm';
import { CustomProject } from '@shared/entities/custom-project.entity';
import { CalculationEngine } from '@api/modules/calculations/calculation.engine';
import { CustomProjectFactory } from '@api/modules/custom-projects/input-factory/custom-project.factory';
Expand All @@ -18,6 +18,7 @@ import { AssumptionsRepository } from '@api/modules/calculations/assumptions.rep
import { User } from '@shared/entities/users/user.entity';
import { EventBus } from '@nestjs/cqrs';
import { SaveCustomProjectEvent } from '@api/modules/custom-projects/events/save-custom-project.event';
import { FetchSpecification } from 'nestjs-base-service';

@Injectable()
export class CustomProjectsService extends AppBaseService<
Expand Down Expand Up @@ -95,4 +96,32 @@ export class CustomProjectsService extends AppBaseService<
async getDefaultAssumptions(dto: GetOverridableAssumptionsDTO) {
return this.assumptionsRepository.getOverridableModelAssumptions(dto);
}

async extendGetByIdQuery(
query: SelectQueryBuilder<CustomProject>,
fetchSpecification?: FetchSpecification,
info?: { user: User },
): Promise<SelectQueryBuilder<CustomProject>> {
const { user } = info;

query
.leftJoinAndSelect('customProject.user', 'user')
.andWhere('user.id = :userId', { userId: user.id });

return query;
}

async extendFindAllQuery(
query: SelectQueryBuilder<CustomProject>,
fetchSpecification: FetchSpecification,
info?: { user: User },
): Promise<SelectQueryBuilder<CustomProject>> {
const { user } = info;

query
.leftJoinAndSelect('customProject.user', 'user')
.andWhere('user.id = :userId', { userId: user.id });

return query;
}
}
120 changes: 120 additions & 0 deletions api/test/integration/custom-projects/custom-projects-read.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { TestManager } from '../../utils/test-manager';
import { customProjectContract } from '@shared/contracts/custom-projects.contract';
import { CustomProject } from '@shared/entities/custom-project.entity';
import { User } from '@shared/entities/users/user.entity';

describe('Read Custom projects', () => {
let testManager: TestManager;
let jwtToken: string;
let user: User;

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

beforeEach(async () => {
({ jwtToken, user } = await testManager.setUpTestUser());
await testManager.ingestCountries();
await testManager.ingestExcel(jwtToken);
});

afterEach(async () => {
await testManager.clearDatabase();
});

afterAll(async () => {
await testManager.close();
});

describe('Read Custom Projects', () => {
test('An anonymous user should be UNAUTHORIZED to read a custom project by its id', async () => {
// Given
const customProject = await testManager.mocks().createCustomProject({
user: { id: user.id } as User,
});

// When
const response = await testManager
.request()
.get(
`${customProjectContract.getCustomProject.path.replace(':id', customProject.id)}`,
)
.send();

// Then
expect(response.status).toBe(401);
expect(response.body.errors).toBeDefined();
});

test('An anonymous user should be UNAUTHORIZED to read any custom projects', async () => {
// Given
const customProject = await testManager.mocks().createCustomProject({
user: { id: user.id } as User,
});

// When
const response = await testManager
.request()
.get(
`${customProjectContract.getCustomProject.path.replace(':id', customProject.id)}`,
)
.send();

// Then
expect(response.status).toBe(401);
expect(response.body.errors).toBeDefined();
});

test('An authenticated user should be able to read one of its custom projects by id', async () => {
// Given
const customProject = await testManager.mocks().createCustomProject({
user: { id: user.id } as User,
});

// When
const response = await testManager
.request()
.get(
`${customProjectContract.getCustomProject.path.replace(':id', customProject.id)}`,
)
.set('Authorization', `Bearer ${jwtToken}`)
.send();

// Then
expect(response.status).toBe(200);
expect(response.body.data.id).toBe(customProject.id);
});

test('An authenticated user should be able to retrieve its custom projects', async () => {
// Given
const { createCustomProject } = testManager.mocks();
const [customProject1, customProject2] = await Promise.all([
createCustomProject({
user: { id: user.id } as User,
}),
createCustomProject({
id: '2d8fdf38-3295-4970-b194-af503a2a6039',
user: { id: user.id } as User,
}),
]);

// When
const response = await testManager
.request()
.get(customProjectContract.getCustomProjects.path)
.set('Authorization', `Bearer ${jwtToken}`)
.send();

// Then
expect(response.status).toBe(200);
const responseData = response.body.data;
expect(responseData.length).toBe(2);
expect(
responseData.find((cp: CustomProject) => cp.id === customProject1.id),
).toBeDefined();
expect(
responseData.find((cp: CustomProject) => cp.id === customProject2.id),
).toBeDefined();
});
});
});
Loading

0 comments on commit 2772c30

Please sign in to comment.