Skip to content

Commit

Permalink
site detail api
Browse files Browse the repository at this point in the history
  • Loading branch information
nikhila-aot committed Apr 10, 2024
1 parent 00695bb commit 18c2840
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 72 deletions.
22 changes: 19 additions & 3 deletions backend/sites/src/app/controllers/site.controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Controller, Get } from '@nestjs/common';
import { Controller, Get, Param } from '@nestjs/common';
import { Unprotected } from 'nest-keycloak-connect';
import { FetchSiteResponse } from '../dto/response/fetchSiteResponse';
import { FetchSiteDetail, FetchSiteResponse } from '../dto/response/fetchSiteResponse';
import { SiteService } from '../services/site.service';

@Controller('site')
Expand All @@ -11,7 +11,7 @@ export class SiteController {
* Get all sites
* @returns all sites
*/
@Get('/') async getSubmission(): Promise<FetchSiteResponse> {
@Get('/') async getAllSites(): Promise<FetchSiteResponse> {
const sites = await this.siteService.findAll();

if (sites?.data.length == 0) {
Expand All @@ -23,4 +23,20 @@ export class SiteController {
return sites;
}

/**
* Get site by site Id
* @returns site matching the site id
*/
@Get('/:siteId') async getSiteBySiteId(@Param('siteId') siteId): Promise<FetchSiteDetail> {
const site = await this.siteService.findSiteBySiteId(siteId);

if (!site) {
return Promise.reject({
statusCode: 404,
message: 'Site data not found',
});
}

return site;
}
}
11 changes: 11 additions & 0 deletions backend/sites/src/app/dto/response/fetchSiteResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,14 @@ export class FetchSiteResponse extends BaseHttpResponse {
@Field(() => [Sites])
data: Sites[];
}

/**
* Class for returing fetch site response from graphql services
*/
@ObjectType()
export class FetchSiteDetail extends BaseHttpResponse {
@Field(() => Sites)
data: Sites;
}


67 changes: 48 additions & 19 deletions backend/sites/src/app/resolvers/site.resolver.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ describe('SiteResolver', () => {
return result;
}),
searchSites: jest.fn(),
findSiteBySiteId: jest.fn(),
},
},
],
Expand All @@ -46,31 +47,59 @@ describe('SiteResolver', () => {
expect(siteService.findAll).toHaveBeenCalled();
});

it('should call siteService.searchSites with the provided searchParam', () => {
const searchParam = 'example';
siteResolver.searchSites(searchParam);
expect(siteService.searchSites).toHaveBeenCalledWith(searchParam);
});
describe('searchSites', () => {
it('should call siteService.searchSites with the provided searchParam', () => {
const searchParam = 'example';
siteResolver.searchSites(searchParam);
expect(siteService.searchSites).toHaveBeenCalledWith(searchParam);
});

it('site search matches a search parameter', async () => {
const searchParam = '123';
const expectedFilteredSites = [sampleSites[0]]; // Only Site 1 matches the searchParam
(siteService.searchSites as jest.Mock).mockResolvedValue(expectedFilteredSites);

const result: Sites[] = await siteResolver.searchSites(searchParam);

it('site search matches a search parameter', async () => {
const searchParam = '123';
const expectedFilteredSites = [sampleSites[0]]; // Only Site 1 matches the searchParam
(siteService.searchSites as jest.Mock).mockResolvedValue(expectedFilteredSites);
expect(siteService.searchSites).toHaveBeenCalledWith(searchParam);
expect(result).toEqual(expectedFilteredSites);
});

const result: Sites[] = await siteResolver.searchSites(searchParam);
it('site search has no matches with the search parameter', async () => {
const searchParam = 'example';
const expectedFilteredSites: Sites[] = [];
(siteService.searchSites as jest.Mock).mockResolvedValue(expectedFilteredSites);

expect(siteService.searchSites).toHaveBeenCalledWith(searchParam);
expect(result).toEqual(expectedFilteredSites);
const result: Sites[] = await siteResolver.searchSites(searchParam);

expect(siteService.searchSites).toHaveBeenCalledWith(searchParam);
expect(result).toEqual(expectedFilteredSites);
});
});

it('site search has no matches with the search parameter', async () => {
const searchParam = 'example';
const expectedFilteredSites: Sites[] = [];
(siteService.searchSites as jest.Mock).mockResolvedValue(expectedFilteredSites);
describe('findSiteBySiteId', () => {
it('should call siteService.findSiteBySiteId with the provided siteId', () => {
const siteId = '123';
siteResolver.findSiteBySiteId(siteId);
expect(siteService.findSiteBySiteId).toHaveBeenCalledWith(siteId);
});

it('finds a matching site id', async () => {
const expectedResult = [sampleSites[0]];
const siteId = '123';

(siteService.findSiteBySiteId as jest.Mock).mockResolvedValue(expectedResult);
const result = await siteResolver.findSiteBySiteId(siteId);
expect(result).toEqual(expectedResult);
});

const result: Sites[] = await siteResolver.searchSites(searchParam);
it('has no matches with the site Id parameter', async () => {
const expectedResult = undefined
const siteId = '111';

expect(siteService.searchSites).toHaveBeenCalledWith(searchParam);
expect(result).toEqual(expectedFilteredSites);
(siteService.findSiteBySiteId as jest.Mock).mockResolvedValue(expectedResult);
const result = await siteResolver.findSiteBySiteId(siteId);
expect(result).toEqual(expectedResult);
});
});
});
6 changes: 6 additions & 0 deletions backend/sites/src/app/resolvers/site.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,10 @@ export class SiteResolver {
searchSites(@Args('searchParam', { type: () => String }) searchParam: string) {
return this.siteService.searchSites(searchParam);
}

@Roles({ roles: ['site-admin'], mode: RoleMatchingMode.ANY })
@Query(() => Sites, { name: 'findSiteBySiteId' })
findSiteBySiteId(@Args('siteId', { type: () => String }) siteId: string) {
return this.siteService.findSiteBySiteId(siteId);
}
}
131 changes: 82 additions & 49 deletions backend/sites/src/app/services/site.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { SiteService } from './site.service';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository, SelectQueryBuilder } from 'typeorm';
import { Sites } from '../entities/sites.entity';
import { FetchSiteDetail } from '../dto/response/fetchSiteResponse';
import { sampleSites } from '../mockData/site.mockData';

describe('SiteService', () => {
let service: SiteService;
let siteService: SiteService;
let siteRepository: Repository<Sites>;

beforeEach(async () => {
Expand All @@ -30,13 +32,16 @@ describe('SiteService', () => {
{ id: '121', commonName: 'duncan' },
{ id: '222', commonName: 'vancouver' }]),
})),
findOneOrFail: jest.fn(() => {
return { id: '123', region_name: 'victoria' };
}),
},
},
],
}).compile();


service = module.get<SiteService>(SiteService);
siteService = module.get<SiteService>(SiteService);
siteRepository = module.get<Repository<Sites>>(getRepositoryToken(Sites));
});

Expand All @@ -45,58 +50,86 @@ describe('SiteService', () => {
});

it('Atleast one site should be returned', async () => {
const sites = await service.findAll();
const sites = await siteService.findAll();
expect(sites.data.length).toBe(3);
});

describe('searchSites', () => {
it('site search matches a search parameter', async () => {
// Arrange
const searchParam = 'v';
const expectedResult = [
{ id: '123', commonName: 'victoria' },
{ id: '222', commonName: 'vancouver' }]; // Example result
const whereMock = jest.fn().mockReturnThis();
const orWhereMock = jest.fn().mockReturnThis();
const getManyMock = jest.fn().mockResolvedValue(expectedResult);
const siteRepositoryFindSpy = jest.spyOn(siteRepository, 'createQueryBuilder').mockReturnValue({
where: whereMock,
orWhere: orWhereMock,
getMany: getManyMock,
} as unknown as SelectQueryBuilder<Sites>);

// Act
const result = await siteService.searchSites(searchParam);

// Assert
expect(siteRepositoryFindSpy).toHaveBeenCalledWith('sites');
expect(whereMock).toHaveBeenCalledWith(expect.any(String), { searchParam: `%${searchParam}%` });
expect(orWhereMock).toHaveBeenCalledTimes(7); // Number of orWhere calls
expect(getManyMock).toHaveBeenCalled();
expect(result).toEqual(expectedResult);
});

it('site search has no matches with the search parameter', async () => {
// Arrange
const searchParam = 'xyz';
const expectedResult = []; // Example result
const whereMock = jest.fn().mockReturnThis();
const orWhereMock = jest.fn().mockReturnThis();
const getManyMock = jest.fn().mockResolvedValue(expectedResult);
const siteRepositoryFindSpy = jest.spyOn(siteRepository, 'createQueryBuilder').mockReturnValue({
where: whereMock,
orWhere: orWhereMock,
getMany: getManyMock,
} as unknown as SelectQueryBuilder<Sites>);

// Act
const result = await siteService.searchSites(searchParam);

it('site search matches a search parameter', async () => {
// Arrange
const searchParam = 'v';
const expectedResult = [
{ id: '123', commonName: 'victoria' },
{ id: '222', commonName: 'vancouver' }]; // Example result
const whereMock = jest.fn().mockReturnThis();
const orWhereMock = jest.fn().mockReturnThis();
const getManyMock = jest.fn().mockResolvedValue(expectedResult);
const siteRepositoryFindSpy = jest.spyOn(siteRepository, 'createQueryBuilder').mockReturnValue({
where: whereMock,
orWhere: orWhereMock,
getMany: getManyMock,
} as unknown as SelectQueryBuilder<Sites>);

// Act
const result = await service.searchSites(searchParam);

// Assert
expect(siteRepositoryFindSpy).toHaveBeenCalledWith('sites');
expect(whereMock).toHaveBeenCalledWith(expect.any(String), { searchParam: `%${searchParam}%` });
expect(orWhereMock).toHaveBeenCalledTimes(7); // Number of orWhere calls
expect(getManyMock).toHaveBeenCalled();
expect(result).toEqual(expectedResult);
// Assert
expect(siteRepositoryFindSpy).toHaveBeenCalledWith('sites');
expect(whereMock).toHaveBeenCalledWith(expect.any(String), { searchParam: `%${searchParam}%` });
expect(orWhereMock).toHaveBeenCalledTimes(7); // Number of orWhere calls
expect(getManyMock).toHaveBeenCalled();
expect(result).toEqual(expectedResult);
});
});

it('site search has no matches with the search parameter', async () => {
// Arrange
const searchParam = 'xyz';
const expectedResult = []; // Example result
const whereMock = jest.fn().mockReturnThis();
const orWhereMock = jest.fn().mockReturnThis();
const getManyMock = jest.fn().mockResolvedValue(expectedResult);
const siteRepositoryFindSpy = jest.spyOn(siteRepository, 'createQueryBuilder').mockReturnValue({
where: whereMock,
orWhere: orWhereMock,
getMany: getManyMock,
} as unknown as SelectQueryBuilder<Sites>);

// Act
const result = await service.searchSites(searchParam);

// Assert
expect(siteRepositoryFindSpy).toHaveBeenCalledWith('sites');
expect(whereMock).toHaveBeenCalledWith(expect.any(String), { searchParam: `%${searchParam}%` });
expect(orWhereMock).toHaveBeenCalledTimes(7); // Number of orWhere calls
expect(getManyMock).toHaveBeenCalled();
expect(result).toEqual(expectedResult);
describe('findSiteBySiteId', () => {
it('should call findOneOrFail method of the repository with the provided siteId', async () => {
const siteId = '123';
await siteService.findSiteBySiteId(siteId);
expect(siteRepository.findOneOrFail).toHaveBeenCalledWith({ where: { id: siteId } });
});

it('should return the site when findOneOrFail method of the repository resolves', async () => {
const siteId = '123';
const expectedResult: FetchSiteDetail = { httpStatusCode: 200, data: sampleSites[0] };
(siteRepository.findOneOrFail as jest.Mock).mockResolvedValue(expectedResult);

const result = await siteService.findSiteBySiteId(siteId);

expect(result).toBeInstanceOf(FetchSiteDetail);
expect(result.httpStatusCode).toBe(200);
expect(result.data).toEqual(expectedResult);
});

it('should throw an error when findOneOrFail method of the repository rejects', async () => {
const siteId = '111';
const error = new Error('Site not found');
(siteRepository.findOneOrFail as jest.Mock).mockRejectedValue(error);
await expect(siteService.findSiteBySiteId(siteId)).rejects.toThrowError(error);
});
});
});
17 changes: 16 additions & 1 deletion backend/sites/src/app/services/site.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { FetchSiteResponse } from '../dto/response/fetchSiteResponse';
import { FetchSiteDetail, FetchSiteResponse } from '../dto/response/fetchSiteResponse';
import { Sites } from '../entities/sites.entity';

/**
Expand Down Expand Up @@ -48,4 +48,19 @@ export class SiteService {

return sites;
}

/**
* Find sites by its ID
* @param siteId site Id
* @returns a single site matching the site ID
*/
async findSiteBySiteId(siteId: string) {
const response = new FetchSiteDetail();

response.httpStatusCode = 200;

response.data = await this.siteRepository.findOneOrFail({ where: { id: siteId } });

return response;
}
}

0 comments on commit 18c2840

Please sign in to comment.