import { Injectable } from '@nestjs/common' import { classToPlain, plainToClass } from 'class-transformer' import { format } from 'date-fns' import { isEmpty, isNil, pick } from 'lodash' import { NotFoundError } from '../errors' import { AvailabilityTime, AvailabilityTimeStatus } from '../models' import { HttpService } from '../services/http' import { BaseRepository } from './base.repository' const DATE_FORMAT = 'yyyy-MM-dd' const ATTRIBUTES = ` id reason status time timezone availability_id appointment_id ` const INSERT = ` mutation insertAvailability($availability_time: availability_time_insert_input!) { insert_availability_time_one(object: $availability_time) { ${ATTRIBUTES} } } ` const GET = ` query availability_time($id: uuid!) { availability_time(where: {id: {_eq: $id}}) { ${ATTRIBUTES} } } ` const GET_BY_APPOINTMENT_ID = ` query availability_time($appointment_id: uuid!) { availability_time(where: {appointment_id: {_eq: $appointment_id}}) { ${ATTRIBUTES} } } ` const GET_BY_AVAILABILITY_ID = ` query availability_time($availability_id: uuid!) { availability_time(where: {availability_id: {_eq: $availability_id}}) { ${ATTRIBUTES} } } ` const UPDATE = ` mutation updateAvailabilityTime($id: uuid!, $status: availability_time_status_enum!, $appointment_id: uuid) { update_availability_time_by_pk(pk_columns: {id: $id}, _set: {appointment_id: $appointment_id, status: $status}) { id } } ` const UPDATE_WITH_CHECK_STATUS = ` mutation updateAvailabilityTime($id: uuid!, $status: availability_time_status_enum!, $check_status: availability_time_status_enum) { update_availability_time(where: {id: {_eq:$id}, status: {_eq: $check_status}}, _set: { status: $status}) { affected_rows } } ` const GENERAL_AVAILABLE_TIMES = ` query availableTimes($beginDate: date!, $endDate: date!, $roles: [roles_enum!]!, $countryCode: String!) { available: availability_time_aggregate(where: {availability: {user: {role: {_in: $roles}, country_code: {_eq: $countryCode}}, date: {_gte: $beginDate, _lte: $endDate}}}) { aggregate { count } } } ` const USER_AVAILABLE_TIMES = ` query availableTimes($beginDate: date!, $endDate: date!, $userId: String!) { available: availability_time_aggregate(where: {availability: {user_id: {_eq: $userId}, date: {_gte: $beginDate, _lte: $endDate}}}) { aggregate { count } } } ` const BLOCK_ALL_BY_USER__DATE = (hasDate: boolean) => ` mutation blockAllAvailabilityTimes($userId: String!${hasDate ? ', $date: date!' : ''}) { update_availability_time(where: {availability: {user_id: {_eq: $userId}${hasDate ? ', date: {_eq: $date}' : ''}}${ hasDate ? ', status: {_eq: available}' : '' }}, _set: {status: blocked}) { affected_rows returning { id } } } ` const DELETE = ` mutation deleteAvailabilityTime($id: uuid!) { delete_availability_time_by_pk(id: $id) { id } } ` export type COUNT_AVAILABLE_TIMES = { available: number } @Injectable() export class AvailabilityTimeRepository extends BaseRepository { constructor(protected readonly httpService: HttpService) { super(httpService, 'availability_time') } async create(availabilityTime: AvailabilityTime): Promise { const qv = { availability_time: classToPlain(availabilityTime) } const { data } = await this.fetchGraphQL({ body: { query: INSERT, variables: qv }, }) return data.insert_availability_time_one.id } async updateWhenStatusEqual(book: AvailabilityTime, status: AvailabilityTimeStatus): Promise { const { data } = await this.fetchGraphQL({ body: { query: UPDATE_WITH_CHECK_STATUS, variables: { ...pick(classToPlain(book), ['id', 'status']), check_status: status }, }, }) if (!data?.update_availability_time?.affected_rows) throw new NotFoundError('Availability time not found with the status provided') } async update(availabilityTime: AvailabilityTime): Promise { await this.fetchGraphQL({ body: { query: UPDATE, variables: pick(classToPlain(availabilityTime), ['id', 'appointment_id', 'status']), }, }) } async get(id: string): Promise { const { data } = await this.fetchGraphQL({ body: { query: GET, variables: { id } }, }) return isEmpty(data.availability_time) ? null : plainToClass(AvailabilityTime, data.availability_time[0]) } async getByAppointmentId(appointmentId: string): Promise { const { data } = await this.fetchGraphQL({ body: { query: GET_BY_APPOINTMENT_ID, variables: { appointment_id: appointmentId } }, }) return isEmpty(data.availability_time) ? null : plainToClass(AvailabilityTime, data.availability_time[0]) } async getByAvailabilityId(availabilityId: string): Promise> { const { data } = await this.fetchGraphQL({ body: { query: GET_BY_AVAILABILITY_ID, variables: { availability_id: availabilityId } }, }) return !isEmpty(data?.availability_time) ? data.availability_time.map((availabilityTime: AvailabilityTime) => AvailabilityTime.toInstance(availabilityTime), ) : [] } async getGeneralAvailability( beginDate: Date, endDate: Date, countryCode: string, roles: string[], ): Promise { const { data } = await this.fetchGraphQL({ body: { query: GENERAL_AVAILABLE_TIMES, variables: { beginDate: format(beginDate, DATE_FORMAT), endDate: format(endDate, DATE_FORMAT), countryCode, roles, }, }, }) return { available: data?.available?.aggregate?.count || 0, } } async getUserAvailability(beginDate: Date, endDate: Date, userId: string): Promise { const { data } = await this.fetchGraphQL({ body: { query: USER_AVAILABLE_TIMES, variables: { beginDate: format(beginDate, DATE_FORMAT), endDate: format(endDate, DATE_FORMAT), userId }, }, }) return { available: data?.available?.aggregate?.count || 0, } } async blockAllAvailabilityTimes(userId: string, date?: Date): Promise { await this.fetchGraphQL({ body: { query: BLOCK_ALL_BY_USER__DATE(!isNil(date)), variables: { userId, date }, }, }) } async delete(id: string): Promise { const { data } = await this.fetchGraphQL({ body: { query: DELETE, variables: { id } }, }) return data.delete_availability_time_by_pk.id } }