From 3ecf3404df744d28340f5f0975f5c78bb24151de Mon Sep 17 00:00:00 2001 From: Inga Date: Sun, 12 Jan 2025 22:50:25 +0000 Subject: [PATCH] implemented calendar endpoint; supplied external tests pass --- service/src/app/app.controller.spec.ts | 1 + .../src/calendar/calendar.controller.spec.ts | 12 +++- service/src/calendar/calendar.service.spec.ts | 12 +++- service/src/calendar/calendar.service.ts | 15 ++++- service/src/db/index.ts | 64 +++++++++++++++++++ service/src/types/enums.ts | 15 +++++ 6 files changed, 114 insertions(+), 5 deletions(-) create mode 100644 service/src/types/enums.ts diff --git a/service/src/app/app.controller.spec.ts b/service/src/app/app.controller.spec.ts index 84f9bba..c84c885 100644 --- a/service/src/app/app.controller.spec.ts +++ b/service/src/app/app.controller.spec.ts @@ -14,6 +14,7 @@ describe('AppController', () => { provide: 'dbClient', useValue: { getNumberOfAllSlots: () => Promise.resolve(123), + getAvailableSlots: () => Promise.resolve([]), } satisfies DbClient, }, AppService, diff --git a/service/src/calendar/calendar.controller.spec.ts b/service/src/calendar/calendar.controller.spec.ts index e7eb2cd..cb4127b 100644 --- a/service/src/calendar/calendar.controller.spec.ts +++ b/service/src/calendar/calendar.controller.spec.ts @@ -1,6 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { CalendarController } from './calendar.controller'; import { CalendarService } from './calendar.service'; +import { DbClient } from 'src/db'; describe('CalendarController', () => { let controller: CalendarController; @@ -8,7 +9,16 @@ describe('CalendarController', () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [CalendarController], - providers: [CalendarService], + providers: [ + { + provide: 'dbClient', + useValue: { + getNumberOfAllSlots: () => Promise.resolve(123), + getAvailableSlots: () => Promise.resolve([]), + } satisfies DbClient, + }, + CalendarService, + ], }).compile(); controller = module.get(CalendarController); diff --git a/service/src/calendar/calendar.service.spec.ts b/service/src/calendar/calendar.service.spec.ts index 1927e6a..2d7cce6 100644 --- a/service/src/calendar/calendar.service.spec.ts +++ b/service/src/calendar/calendar.service.spec.ts @@ -1,12 +1,22 @@ import { Test, TestingModule } from '@nestjs/testing'; import { CalendarService } from './calendar.service'; +import { DbClient } from 'src/db'; describe('CalendarService', () => { let service: CalendarService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [CalendarService], + providers: [ + { + provide: 'dbClient', + useValue: { + getNumberOfAllSlots: () => Promise.resolve(123), + getAvailableSlots: () => Promise.resolve([]), + } satisfies DbClient, + }, + CalendarService, + ], }).compile(); service = module.get(CalendarService); diff --git a/service/src/calendar/calendar.service.ts b/service/src/calendar/calendar.service.ts index 9d29df8..598c7ee 100644 --- a/service/src/calendar/calendar.service.ts +++ b/service/src/calendar/calendar.service.ts @@ -1,10 +1,19 @@ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import { QueryParamsDto, QueryResponseDto } from './calendar.dto'; +import { DbClient } from 'src/db'; @Injectable() export class CalendarService { + constructor( + @Inject('dbClient') + private readonly dbClient: DbClient, + ) {} + async getAvailableSlots(queryParamsDto: QueryParamsDto) { - console.log(queryParamsDto); - return Promise.resolve([new QueryResponseDto(1, new Date())]); + const rows = await this.dbClient.getAvailableSlots(queryParamsDto); + return rows.map( + (row) => + new QueryResponseDto(parseInt(row.count, 10), new Date(row.start_date)), + ); } } diff --git a/service/src/db/index.ts b/service/src/db/index.ts index fecaced..7298bba 100644 --- a/service/src/db/index.ts +++ b/service/src/db/index.ts @@ -1,7 +1,17 @@ import { OnModuleDestroy } from '@nestjs/common'; import { Pool } from 'pg'; +interface QueryParams { + language: string; + products: string[]; + rating: string; + date: string; +} + +type QueryResponseRow = Record<'start_date' | 'count', string>; + export interface DbClient { + getAvailableSlots(params: QueryParams): Promise; getNumberOfAllSlots(): Promise; } @@ -25,6 +35,60 @@ export const createDbClient = async ( return parseInt(result.rows[0]?.count ?? '0'); }, + getAvailableSlots: async ({ language, products, rating, date }) => { + const result = await pool.query({ + text: ` + select + available_slots.start_date start_date, + count(*) count + from + slots available_slots + join + sales_managers managers on managers.id = available_slots.sales_manager_id + where + $1 = ANY(managers.languages) + and + managers.products @> $2 + and + $3 = ANY(managers.customer_ratings) + and + available_slots.booked = false + and + available_slots.start_date::timestamp::date = TO_DATE($4, 'YYYY-MM-DD') + and + not exists ( + select * + from slots unavailable_slots + where + unavailable_slots.sales_manager_id = available_slots.sales_manager_id + and + booked = true + and + ( + ( + unavailable_slots.start_date >= available_slots.start_date + and + unavailable_slots.start_date < available_slots.end_date + ) + or + ( + unavailable_slots.end_date > available_slots.start_date + and + unavailable_slots.end_date <= available_slots.end_date + ) + ) + ) + group by + available_slots.start_date + order by + available_slots.start_date asc + `, + values: [language, products, rating, date], + }); + + return result.rows; + }, + onModuleDestroy: async () => { await pool.end(); }, diff --git a/service/src/types/enums.ts b/service/src/types/enums.ts new file mode 100644 index 0000000..7a99df9 --- /dev/null +++ b/service/src/types/enums.ts @@ -0,0 +1,15 @@ +export enum ProductType { + SolarPanels = 'SolarPanels', + Heatpumps = 'Heatpumps', +} + +export enum Language { + German = 'German', + English = 'English', +} + +export enum CustomerRating { + Gold = 'Gold', + Silver = 'Silver', + Bronze = 'Bronze', +}