From 5c731998c3b6e809c8a2f3ecca6c2c9b0a592dfc Mon Sep 17 00:00:00 2001 From: Giuseppe Di Pinto Date: Tue, 23 Apr 2024 17:38:05 +0200 Subject: [PATCH] add service /features api mock --- src/config.ts | 1 + .../services/payloads/get-featured-items.ts | 101 ++++++++++++++++++ src/features/services/routers/featured.ts | 27 +++++ src/features/services/routers/index.ts | 1 + src/features/services/types/configuration.ts | 1 + src/persistence/services.ts | 4 + 6 files changed, 135 insertions(+) create mode 100644 src/features/services/payloads/get-featured-items.ts create mode 100644 src/features/services/routers/featured.ts diff --git a/src/config.ts b/src/config.ts index 0484a2dd..46d3c5f0 100644 --- a/src/config.ts +++ b/src/config.ts @@ -191,6 +191,7 @@ const defaultConfig: IoDevServerConfig = { }, service: { response: { + featuredItemsResponseCode: 200, institutionsResponseCode: 200 } }, diff --git a/src/features/services/payloads/get-featured-items.ts b/src/features/services/payloads/get-featured-items.ts new file mode 100644 index 00000000..f88326f2 --- /dev/null +++ b/src/features/services/payloads/get-featured-items.ts @@ -0,0 +1,101 @@ +import { nonEmptyArray } from "fp-ts"; +import * as A from "fp-ts/Array"; +import * as O from "fp-ts/lib/Option"; +import { pipe } from "fp-ts/lib/function"; +import { FeaturedItem } from "../../../../generated/definitions/services/FeaturedItem"; +import { FeaturedItems } from "../../../../generated/definitions/services/FeaturedItems"; +import ServicesDB from "../../../persistence/services"; +import { getInstitutionsResponsePayload } from "./get-institutions"; + +/** + * Returns a random ordered array subset. + * @param array starting array of type T + * @param size array subset size (if `size` greater than `array`, it returns empty array subset) + * @returns + */ +const getRandomArraySubset = (array: T[], size: number): T[] => + pipe( + O.some(array), + O.fromPredicate(arr => O.isSome(arr) && size <= array.length), + O.fold( + () => [], + () => { + const remainingItems = [...array]; + return pipe( + nonEmptyArray.range(1, size), + A.map(() => { + const randomIndex = Math.floor( + Math.random() * remainingItems.length + ); + const selectedItem = remainingItems[randomIndex]; + // eslint-disable-next-line functional/immutable-data + remainingItems.splice(randomIndex, 1); + return selectedItem; + }) + ); + } + ) + ); + +export const getFeaturedItemsResponsePayload = (): FeaturedItems => { + // take some casual national service + const selectedNationalServices = getRandomArraySubset( + ServicesDB.getNationalServices(), + 1 + ); + // take some casual special service + const selectedSpecialServices = getRandomArraySubset( + ServicesDB.getSpecialServices(), + 3 + ); + // take some casual institutions + const featuredIntitutions = getRandomArraySubset( + Array.from(getInstitutionsResponsePayload().institutions), + 1 + ); + + /** + * Reduced national services to FeaturedService[] (add organization_name for layout testing purpose) + */ + const featuredNationalServices: FeaturedItem[] = pipe( + selectedNationalServices, + A.reduce([] as FeaturedItem[], (accumulator, service) => [ + ...accumulator, + { + id: service.service_id, + name: service.service_name, + version: service.version, + organization_name: service.organization_name + } + ]) + ); + + /** + * Reduce special services to FeaturedService[] + */ + const featuredSpecialServices: FeaturedItem[] = pipe( + selectedSpecialServices, + A.reduce([] as FeaturedItem[], (accumulator, service) => [ + ...accumulator, + { + id: service.service_id, + name: service.service_name, + version: service.version + } + ]) + ); + + // returns randomly ordered featured items + const featuredItems = pipe( + [ + ...featuredSpecialServices, + ...featuredIntitutions, + ...featuredNationalServices + ], + arr => getRandomArraySubset(arr, arr.length) + ); + + return { + items: featuredItems + }; +}; diff --git a/src/features/services/routers/featured.ts b/src/features/services/routers/featured.ts new file mode 100644 index 00000000..ab841b0e --- /dev/null +++ b/src/features/services/routers/featured.ts @@ -0,0 +1,27 @@ +import * as O from "fp-ts/lib/Option"; +import { pipe } from "fp-ts/lib/function"; +import { ioDevServerConfig } from "../../../config"; +import { addHandler } from "../../../payloads/response"; +import { getFeaturedItemsResponsePayload } from "../payloads/get-featured-items"; +import { addApiV2Prefix, serviceRouter } from "./router"; + +const serviceConfig = ioDevServerConfig.features.service; + +// Retrieve featured items +addHandler(serviceRouter, "get", addApiV2Prefix("/featured"), (_, res) => + pipe( + serviceConfig.response.featuredItemsResponseCode, + O.fromPredicate(statusCode => statusCode !== 200), + O.fold( + () => + pipe( + O.of(getFeaturedItemsResponsePayload()), + O.fold( + () => res.status(404), + featuredItems => res.status(200).json(featuredItems) + ) + ), + statusCode => res.sendStatus(statusCode) + ) + ) +); diff --git a/src/features/services/routers/index.ts b/src/features/services/routers/index.ts index 9e98f1bc..a4aed809 100644 --- a/src/features/services/routers/index.ts +++ b/src/features/services/routers/index.ts @@ -1,3 +1,4 @@ +import "./featured"; import "./institutions"; export { serviceRouter } from "./router"; diff --git a/src/features/services/types/configuration.ts b/src/features/services/types/configuration.ts index af7eba9c..8abc5122 100644 --- a/src/features/services/types/configuration.ts +++ b/src/features/services/types/configuration.ts @@ -5,6 +5,7 @@ export const ServiceConfiguration = t.interface({ // configure some API response error code response: t.interface({ // 200 success with payload + featuredItemsResponseCode: HttpResponseCode, institutionsResponseCode: HttpResponseCode }) }); diff --git a/src/persistence/services.ts b/src/persistence/services.ts index 26562c1e..bb8bd073 100644 --- a/src/persistence/services.ts +++ b/src/persistence/services.ts @@ -97,6 +97,8 @@ const getAllServices = () => [ ]; const getLocalServices = () => localServices.map(ls => ({ ...ls })); +const getNationalServices = () => nationalServices.map(ls => ({ ...ls })); +const getSpecialServices = () => specialServices.map(ls => ({ ...ls })); const getPreference = ( serviceId: ServiceId @@ -166,6 +168,8 @@ export default { deleteServices, getAllServices, getLocalServices, + getNationalServices, + getSpecialServices, getPreference, getService, getSummaries,