Skip to content

Commit

Permalink
feat: adds available rooms API
Browse files Browse the repository at this point in the history
  • Loading branch information
zakhaev26 committed Aug 28, 2024
1 parent 2a0df44 commit f8e16cf
Show file tree
Hide file tree
Showing 9 changed files with 242 additions and 13 deletions.
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"dev": "ts-node-dev --no-notify src/",
"start": "yarn run compile && node lib/",
"mocha": "mocha --require ts-node/register --require source-map-support/register \"test/**/*.ts\" --recursive --exit",
"compile": "shx rm -rf lib/ && tsc"
"compile": "shx rm -rf lib/ && tsc",
"ngrok": "ngrok http --domain=bat-giving-roughy.ngrok-free.app 3030"
},
"standard": {
"env": [
Expand Down Expand Up @@ -84,5 +85,9 @@
"ts-jest": "^29.1.2",
"ts-node-dev": "^2.0.0",
"typescript": "^4.7.3"
},
"volta": {
"node": "20.16.0",
"yarn": "1.22.22"
}
}
129 changes: 129 additions & 0 deletions src/services/available-rooms/available-rooms.class.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import {
Id,
NullableId,
Paginated,
Params,
ServiceMethods,
} from "@feathersjs/feathers";
import { Application } from "../../declarations";
import { BadRequest } from "@feathersjs/errors";

interface Data {}

interface ServiceOptions {}

export class AvailableRooms implements ServiceMethods<Data> {
app: Application;
options: ServiceOptions;

constructor(options: ServiceOptions = {}, app: Application) {
this.options = options;
this.app = app;
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
async find(params?: Params): Promise<Data[] | Paginated<Data>> {
/*
* @description
* This Endpoint returns all the available rooms (status == Available ) between a startDate &
* endDate. The Handler queries (agg.) the db to search for rooms between startDate and endDate and
* sends the result to client as response.
* */

interface QueryInterface {
startDate?: Date;
endDate?: Date;
}

const { startDate, endDate } = params?.query as QueryInterface;

if (!startDate || !endDate)
throw new BadRequest("Please provide startDate and endDate");

const start = new Date(startDate);
const end = new Date(endDate);

if (isNaN(start.getTime()) || isNaN(end.getTime())) {
throw new BadRequest("Invalid date format");
}

const roomsModel = this.app.service("rooms").Model;

// rooms that are not booked during the specified dates
const roomsWithOrWithoutBookings = await roomsModel
.aggregate([
{
$lookup: {
from: "bookings",
localField: "_id",
foreignField: "room",
as: "bookings",
},
},
{
$match: {
$or: [
{ bookings: { $size: 0 } },
{
bookings: {
$not: {
$elemMatch: {
dates: {
$not: {
$elemMatch: {
$lt: end,
$gte: start,
},
},
},
},
},
},
},
],
},
},
])
.exec();

const resp = roomsWithOrWithoutBookings.map((room: any) => {
if (room.bookings.length === 0) {
delete room.bookings;
return room;
}
});

return resp;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async get(id: Id, params?: Params): Promise<Data> {
return {
id,
text: `A new message with ID: ${id}!`,
};
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
async create(data: Data, params?: Params): Promise<Data> {
if (Array.isArray(data)) {
return Promise.all(data.map((current) => this.create(current, params)));
}

return data;
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
async update(id: NullableId, data: Data, params?: Params): Promise<Data> {
return data;
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
async patch(id: NullableId, data: Data, params?: Params): Promise<Data> {
return data;
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
async remove(id: NullableId, params?: Params): Promise<Data> {
return { id };
}
}
37 changes: 37 additions & 0 deletions src/services/available-rooms/available-rooms.hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { HooksObject } from '@feathersjs/feathers';
import * as authentication from '@feathersjs/authentication';
// Don't remove this comment. It's needed to format import lines nicely.

const { authenticate } = authentication.hooks;

export default {
before: {
all: [ authenticate('jwt') ],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
},

after: {
all: [],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
},

error: {
all: [],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
}
};
26 changes: 26 additions & 0 deletions src/services/available-rooms/available-rooms.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Initializes the `available-rooms` service on path `/available-rooms`
import { ServiceAddons } from '@feathersjs/feathers';
import { Application } from '../../declarations';
import { AvailableRooms } from './available-rooms.class';
import hooks from './available-rooms.hooks';

// Add this service to the service type index
declare module '../../declarations' {
interface ServiceTypes {
'available-rooms': AvailableRooms & ServiceAddons<any>;
}
}

export default function (app: Application): void {
const options = {
paginate: app.get('paginate')
};

// Initialize our service with any options it requires
app.use('/available-rooms', new AvailableRooms(options, app));

// Get our initialized service so that we can register hooks
const service = app.service('available-rooms');

service.hooks(hooks);
}
2 changes: 1 addition & 1 deletion src/services/bookings/bookings.hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default {
// @ts-expect-error old defs in .d.ts lib files
find: [iff(isUserType, setQuery("user", "_id")), handleSoftDelete()],
// @ts-expect-error old defs in .d.ts lib files
get: [iff(isUserType, setQuery("user")), handleSoftDelete()],
get: [iff(isUserType, setQuery("user", "_id")), handleSoftDelete()],
/**
* @zakhaev26
* @todo
Expand Down
2 changes: 2 additions & 0 deletions src/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import forgotPassword from './forgot-password/forgot-password.service';
import sendOtp from './send-otp/send-otp.service';
import otp from './otp/otp.service';
import updateBookingStatus from './update-booking-status/update-booking-status.service';
import availableRooms from './available-rooms/available-rooms.service';
// Don't remove this comment. It's needed to format import lines nicely.

export default function (app: Application): void {
Expand All @@ -20,4 +21,5 @@ export default function (app: Application): void {
app.configure(otp);
app.configure(sendOtp);
app.configure(updateBookingStatus);
app.configure(availableRooms);
}
13 changes: 13 additions & 0 deletions src/services/update-booking-status/isValidStatusMove.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import BookingStatus from '../../constants/booking-status.enum';

const isValidRoomStatusMove = (currentStatus: string, newStatus: string): boolean => {
// Define the valid status transitions
const validTransitions: Record<string, string[]> = {
[BookingStatus.PENDING]: [BookingStatus.APPROVED,BookingStatus.CANCELLED],
[BookingStatus.CANCELLED]: [],
[BookingStatus.APPROVED]: [],
};
return validTransitions[currentStatus].includes(newStatus);
};

export default isValidRoomStatusMove;
31 changes: 20 additions & 11 deletions src/services/update-booking-status/update-booking-status.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import {
Paginated,
Params,
ServiceMethods,
} from "@feathersjs/feathers";
import { Application } from "../../declarations";
import { BadRequest, NotAuthenticated } from "@feathersjs/errors";
import RolesEnum from "../../constants/roles.enum";
import BookingStatus from "../../constants/booking-status.enum";
} from '@feathersjs/feathers';
import { Application } from '../../declarations';
import { BadRequest, NotAuthenticated } from '@feathersjs/errors';
import RolesEnum from '../../constants/roles.enum';
import BookingStatus from '../../constants/booking-status.enum';
import isValidStatusMove from './isValidStatusMove';

interface Data {}
interface CreateDTO {
Expand Down Expand Up @@ -62,19 +63,27 @@ export class UpdateBookingStatus implements ServiceMethods<Data> {
if (!params || !params.user) throw new NotAuthenticated();
if (![RolesEnum.SUPER_ADMIN].includes(params.user.type))
throw new BadRequest(
"Only Super Admins are allowed to perform this task."
'Only Super Admins are allowed to perform this task.'
);

if (!id) {
throw new BadRequest("Please provide the Booking ID to be updated.");
throw new BadRequest('Please provide the Booking ID to be updated.');
}

const currentBooking = await this.app.service('bookings')._get(id);
if(!currentBooking) throw new BadRequest('Booking does not exist!');


if(data.status && !isValidStatusMove(currentBooking.status, data.status)) {
throw new BadRequest('Invalid Status Move!');
}

const reqBody: Record<string, any> = {};
if (data.paid) reqBody["paid"] = data.paid;
if (data.status) reqBody["status"] = data.status;
if (data.paid) reqBody['paid'] = data.paid;
if (data.status) reqBody['status'] = data.status;

reqBody["lastManagedBy"] = params.user._id;
const resp = await this.app.service("bookings")._patch(id, reqBody);
reqBody['lastManagedBy'] = params.user._id;
const resp = await this.app.service('bookings')._patch(id, reqBody);
return resp;
}

Expand Down
8 changes: 8 additions & 0 deletions test/services/available-rooms.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import app from '../../src/app';

describe('\'available-rooms\' service', () => {
it('registered the service', () => {
const service = app.service('available-rooms');
expect(service).toBeTruthy();
});
});

0 comments on commit f8e16cf

Please sign in to comment.