@windingtree/wt-read-api
Version:
API to interact with the Winding Tree platform
278 lines (267 loc) • 9.75 kB
JavaScript
const { Http404Error, HttpValidationError } = require('../errors');
const { DataFormatValidator } = require('../services/validation');
const wtJsLibs = require('../services/wt-js-libs');
const { formatError } = require('../services/utils');
const { config } = require('../config');
const {
VALIDATION_WARNING_HEADER,
SCHEMA_PATH,
ROOM_TYPE_MODEL,
RATE_PLAN_MODEL,
AVAILABILITY_MODEL,
} = require('../constants');
const detectRatePlans = (roomTypeId, ratePlansArray) => {
return ratePlansArray.filter((rp) => rp.roomTypeIds && rp.roomTypeIds.indexOf(roomTypeId) > -1);
};
const detectAvailability = (roomTypeId, availabilityObject) => {
const availabilities = availabilityObject.roomTypes.filter((a) => {
return a.roomTypeId === roomTypeId;
});
return {
updatedAt: availabilityObject && availabilityObject.updatedAt,
roomTypes: availabilities,
};
};
const getHotelFields = (fieldsArray) => {
const resolvedFields = ['descriptionUri.roomTypes'];
if (fieldsArray.indexOf('ratePlans') > -1) {
resolvedFields.push('ratePlansUri');
}
if (fieldsArray.indexOf('availability') > -1) {
resolvedFields.push('availabilityUri');
}
resolvedFields.push('guarantee');
return resolvedFields;
};
const setAdditionalFields = (roomType, apiContents, fieldsQuery) => {
if (fieldsQuery.indexOf('ratePlans') > -1) {
if (apiContents.ratePlansUri) {
roomType.ratePlans = detectRatePlans(roomType.id, apiContents.ratePlansUri.contents);
} else {
roomType.ratePlans = [];
}
}
if (fieldsQuery.indexOf('availability') > -1) {
if (apiContents.availabilityUri) {
roomType.availability = detectAvailability(roomType.id, apiContents.availabilityUri.contents);
} else {
roomType.availability = [];
}
}
return roomType;
};
const _normalizeQuery = (fieldsQuery) => {
const fieldsArray = Array.isArray(fieldsQuery) ? fieldsQuery : fieldsQuery.split(',');
return fieldsArray.filter((x) => !!x);
};
const findAll = async (req, res, next) => {
const fieldsQuery = req.query.fields || [];
const fieldsArray = _normalizeQuery(fieldsQuery);
try {
const hotelApis = await res.locals.wt.hotel.getWindingTreeApi();
const apiContents = (await hotelApis.hotel[0].toPlainObject(getHotelFields(fieldsArray))).contents;
const passesTrustworthinessTest = await wtJsLibs.passesTrustworthinessTest(res.locals.wt.hotel.address, apiContents.guarantee);
if (!passesTrustworthinessTest) {
return next(new Http404Error('hotelNotFound', 'Hotel does not pass the trustworthiness test.', 'Hotel not found'));
}
const roomTypes = apiContents.descriptionUri.contents.roomTypes;
const items = [], warnings = [], errors = [];
const swaggerDocument = await DataFormatValidator.loadSchemaFromPath(SCHEMA_PATH, ROOM_TYPE_MODEL);
for (let roomType of roomTypes) {
roomType = setAdditionalFields(roomType, apiContents, fieldsQuery);
try {
DataFormatValidator.validate(
roomType,
ROOM_TYPE_MODEL,
swaggerDocument.components.schemas,
config.dataFormatVersions.hotels,
apiContents.dataFormatVersion,
'room type'
);
items.push(roomType);
} catch (e) {
if (e instanceof HttpValidationError) {
const err = formatError(e);
err.data = roomType;
if (e.data && e.data.valid) {
warnings.push(err);
} else {
err.data = { id: err.data.id };
errors.push(err);
}
} else {
next(e);
}
}
}
res.status(200).json({
items,
warnings,
errors,
});
} catch (e) {
next(e);
}
};
const find = async (req, res, next) => {
const { roomTypeId } = req.params;
const fieldsQuery = req.query.fields || [];
const fieldsArray = _normalizeQuery(fieldsQuery);
try {
const hotelApis = await res.locals.wt.hotel.getWindingTreeApi();
const apiContents = (await hotelApis.hotel[0].toPlainObject(getHotelFields(fieldsArray))).contents;
const passesTrustworthinessTest = await wtJsLibs.passesTrustworthinessTest(res.locals.wt.hotel.address, apiContents.guarantee);
if (!passesTrustworthinessTest) {
return next(new Http404Error('hotelNotFound', 'Hotel does not pass the trustworthiness test.', 'Hotel not found'));
}
const roomTypes = apiContents.descriptionUri.contents.roomTypes;
let roomType = roomTypes.find((rt) => { return rt.id === roomTypeId; });
if (!roomType) {
return next(new Http404Error('roomTypeNotFound', 'Room type not found'));
}
roomType = setAdditionalFields(roomType, apiContents, fieldsQuery);
const swaggerDocument = await DataFormatValidator.loadSchemaFromPath(SCHEMA_PATH, ROOM_TYPE_MODEL, fieldsArray.length === 0 ? undefined : fieldsArray);
try {
DataFormatValidator.validate(
roomType,
ROOM_TYPE_MODEL,
swaggerDocument.components.schemas,
config.dataFormatVersions.hotels,
apiContents.dataFormatVersion,
'room type'
);
} catch (e) {
if (e instanceof HttpValidationError) {
const err = formatError(e);
err.data = roomType;
if (e.data && e.data.valid) {
return res.set(VALIDATION_WARNING_HEADER, e.data.errors).status(200).json(err.toPlainObject());
} else {
return res.status(err.status).json(err.toPlainObject());
}
} else {
next(e);
}
}
res.status(200).json(roomType);
} catch (e) {
next(e);
}
};
const findRatePlans = async (req, res, next) => {
const { roomTypeId } = req.params;
try {
const hotelApis = await res.locals.wt.hotel.getWindingTreeApi();
const apiContents = (await hotelApis.hotel[0].toPlainObject(getHotelFields(['ratePlans']))).contents;
const passesTrustworthinessTest = await wtJsLibs.passesTrustworthinessTest(res.locals.wt.hotel.address, apiContents.guarantee);
if (!passesTrustworthinessTest) {
return next(new Http404Error('hotelNotFound', 'Hotel does not pass the trustworthiness test.', 'Hotel not found'));
}
const roomTypes = apiContents.descriptionUri.contents.roomTypes;
const roomType = roomTypes.find((rt) => { return rt.id === roomTypeId; });
if (!roomType) {
return next(new Http404Error('roomTypeNotFound', 'Room type not found'));
}
if (!apiContents.ratePlansUri) {
return next(new Http404Error('noRatePlans', 'No ratePlansUri specified.'));
}
const ratePlans = detectRatePlans(roomTypeId, apiContents.ratePlansUri.contents);
const items = [], warnings = [], errors = [];
const swaggerDocument = await DataFormatValidator.loadSchemaFromPath(SCHEMA_PATH, RATE_PLAN_MODEL);
for (const plan of ratePlans) {
try {
DataFormatValidator.validate(
plan,
RATE_PLAN_MODEL,
swaggerDocument.components.schemas,
config.dataFormatVersions.hotels,
apiContents.dataFormatVersion,
'rate plan',
);
items.push(plan);
} catch (e) {
if (e instanceof HttpValidationError) {
const err = formatError(e);
err.data = plan;
if (e.data && e.data.valid) {
warnings.push(err);
} else {
err.data = { id: err.data.id };
errors.push(err);
}
} else {
next(e);
}
}
}
res.status(200).json({
items,
warnings,
errors,
});
} catch (e) {
next(e);
}
};
const findAvailability = async (req, res, next) => {
const { roomTypeId } = req.params;
try {
const hotelApis = await res.locals.wt.hotel.getWindingTreeApi();
const apiContents = (await hotelApis.hotel[0].toPlainObject(getHotelFields(['availability']))).contents;
const passesTrustworthinessTest = await wtJsLibs.passesTrustworthinessTest(res.locals.wt.hotel.address, apiContents.guarantee);
if (!passesTrustworthinessTest) {
return next(new Http404Error('hotelNotFound', 'Hotel does not pass the trustworthiness test.', 'Hotel not found'));
}
const roomTypes = apiContents.descriptionUri.contents.roomTypes;
const roomType = roomTypes.find((rt) => { return rt.id === roomTypeId; });
if (!roomType) {
return next(new Http404Error('roomTypeNotFound', 'Room type not found'));
}
if (!apiContents.availabilityUri) {
return next(new Http404Error('noAvailability', 'No availabilityUri specified.'));
}
const availability = detectAvailability(roomTypeId, apiContents.availabilityUri.contents);
const items = [], warnings = [], errors = [];
const swaggerDocument = await DataFormatValidator.loadSchemaFromPath(SCHEMA_PATH, AVAILABILITY_MODEL);
for (const roomType of availability.roomTypes) {
try {
DataFormatValidator.validate(
roomType,
AVAILABILITY_MODEL,
swaggerDocument.components.schemas,
config.dataFormatVersions.hotels,
apiContents.dataFormatVersion,
'availability',
);
items.push(roomType);
} catch (e) {
if (e instanceof HttpValidationError) {
const err = formatError(e);
err.data = roomType;
if (e.data && e.data.valid) {
warnings.push(err);
} else {
err.data = { id: err.data.id };
errors.push(err);
}
} else {
next(e);
}
}
}
res.status(200).json({
items,
warnings,
errors,
updatedAt: availability.updatedAt,
});
} catch (e) {
next(e);
}
};
module.exports = {
findAll,
find,
findRatePlans,
findAvailability,
};