Skip to content

Commit

Permalink
add institutions api
Browse files Browse the repository at this point in the history
  • Loading branch information
adelloste committed Apr 13, 2024
1 parent 3c10a43 commit 5621fca
Show file tree
Hide file tree
Showing 12 changed files with 219 additions and 38 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"api_fast_login": "https://raw.githubusercontent.com/pagopa/io-backend/v13.32.1-RELEASE/openapi/generated/api_fast_login.yaml",
"api_pagopa_walletv3": "https://raw.githubusercontent.com/pagopa/pagopa-infra/bec0f57606a2002706bd5f5286fad3e508d2f50e/src/domains/wallet-app/api/payment-wallet/v1/_openapi.json.tpl",
"api_pagopa_ecommerce": "https://raw.githubusercontent.com/pagopa/pagopa-infra/2adee4c0a8de8570e74710379f9cccb04c6925b6/src/domains/ecommerce-app/api/ecommerce-io/v1/_openapi.json.tpl",
"api_services": "https://raw.githubusercontent.com/pagopa/io-services-cms/master/apps/app-backend/openapi.yaml",
"author": "Matteo Boschi",
"license": "MIT",
"private": false,
Expand Down Expand Up @@ -55,6 +56,7 @@
"generate:idpay-definitions": "rimraf generated/definitions/idpay && mkdir -p generated/definitions/idpay && gen-api-models --api-spec $npm_package_api_idpay --out-dir ./generated/definitions/idpay --no-strict",
"generate:fast-login-definitions": "rimraf generated/definitions/fast_login && mkdir -p generated/definitions/fast_login && gen-api-models --api-spec $npm_package_api_fast_login --out-dir ./generated/definitions/fast_login --no-strict --request-types --response-decoders",
"generate:pagopa": "npm-run-all generate:pagopa-walletv2-definitions generate:pagopa-privative-configuration-definitions generate:pagopa-cobadge-configuration-definitions generate:pagopa-walletv3-definitions generate:pagopa-ecommerce-definitions",
"generate:services-definitions": "rimraf generated/definitions/services && mkdir -p generated/definitions/services && gen-api-models --api-spec $npm_package_api_services --out-dir ./generated/definitions/services --no-strict",
"generate": "npm-run-all generate:*"
},
"jest": {
Expand Down
5 changes: 5 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ const defaultConfig: IoDevServerConfig = {
fastLogin: {
sessionTTLinMS: 60000
},
service: {
response: {
institutionsResponseCode: 200
}
},
allowRandomValues: true
}
};
Expand Down
1 change: 1 addition & 0 deletions src/features/services/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { serviceRouter } from "./routers";
73 changes: 73 additions & 0 deletions src/features/services/payloads/get-institutions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import * as A from "fp-ts/lib/Array";
import { identity, pipe } from "fp-ts/lib/function";
import _ from "lodash";
import { ServicePublic } from "../../../../generated/definitions/backend/ServicePublic";
import { ServiceScopeEnum } from "../../../../generated/definitions/backend/ServiceScope";
import { Institution } from "../../../../generated/definitions/services/Institution";
import { InstitutionsResource } from "../../../../generated/definitions/services/InstitutionsResource";
import ServicesDB from "../../../persistence/services";

const filterByScope = (service: ServicePublic, scope?: ServiceScopeEnum) => {
if (!scope) {
return true;
}
return service.service_metadata?.scope === scope;
};

const filterBySearch = (service: ServicePublic, search?: string) => {
if (!search) {
return true;
}
return service.service_name.toLowerCase().includes(search);
};

// services?offset=0&limit=20
// services?offset=20&limit=20
// services?offset=40&limit=20
export const getInstitutionsResponsePayload = (
limit: number = 20,
offset: number = 0,
scope?: ServiceScopeEnum,
search?: string
): InstitutionsResource => {
const filteredInstitutions = pipe(
ServicesDB.getAllServices(),
A.reduce([] as Institution[], (accumulator, service) => {
const isValidService = pipe(
[
(service: ServicePublic) => filterByScope(service, scope),
(service: ServicePublic) => filterBySearch(service, search)
],
A.flap(service),
A.every(identity)
);

if (isValidService) {
return [
...accumulator,
{
id: service.service_id,
name: service.service_name,
fiscal_code: service.organization_fiscal_code
}
];
}

return accumulator;
})
);

const totalElements = filteredInstitutions.length;
const startIndex = offset;
const endIndex = offset + limit;
const istitutionList = _.slice(filteredInstitutions, startIndex, endIndex);

const response: InstitutionsResource = {
institutions: istitutionList,
limit,
offset,
count: totalElements
};

return response;
};
3 changes: 3 additions & 0 deletions src/features/services/routers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import "./institutions";

export { serviceRouter } from "./router";
52 changes: 52 additions & 0 deletions src/features/services/routers/institutions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { sequenceT } from "fp-ts/lib/Apply";
import * as O from "fp-ts/lib/Option";
import { pipe } from "fp-ts/lib/function";
import { addHandler } from "../../../payloads/response";
import { addApiV1Prefix } from "../../../utils/strings";
import { ServiceScope } from "../../../../generated/definitions/backend/ServiceScope";
import { getInstitutionsResponsePayload } from "../payloads/get-institutions";
import { ioDevServerConfig } from "../../../config";
import { serviceRouter } from "./router";

type Query = string | qs.ParsedQs | string[] | qs.ParsedQs[] | undefined;

const extractQuery = (query: Query) =>
pipe(
query,
O.fromNullable,
O.map(s => parseInt(s as string, 10)),
O.toUndefined
);

addHandler(serviceRouter, "get", addApiV1Prefix("/institutions"), (req, res) =>
pipe(
ioDevServerConfig.features.service.response.institutionsResponseCode,
O.fromPredicate(statusCode => statusCode !== 200),
O.fold(
() =>
pipe(
sequenceT(O.Monad)(
O.of(pipe(req.query.limit, extractQuery)),
O.of(pipe(req.query.offset, extractQuery)),
O.of(
pipe(
req.query.scope,
ServiceScope.decode,
O.fromEither,
O.toUndefined
)
),
O.of(
pipe(req.query.search as string, O.fromNullable, O.toUndefined)
)
),
O.map(args => getInstitutionsResponsePayload(...args)),
O.fold(
() => res.status(404),
instituitions => res.status(200).json(instituitions)
)
),
statusCode => res.sendStatus(statusCode)
)
)
);
3 changes: 3 additions & 0 deletions src/features/services/routers/router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Router } from "express";

export const serviceRouter = Router();
12 changes: 12 additions & 0 deletions src/features/services/types/configuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import * as t from "io-ts";
import { HttpResponseCode } from "../../../types/httpResponseCode";

export const ServiceConfiguration = t.interface({
// configure some API response error code
response: t.interface({
// 200 success with payload
institutionsResponseCode: HttpResponseCode
})
});

export type ServiceConfiguration = t.TypeOf<typeof ServiceConfiguration>;
7 changes: 7 additions & 0 deletions src/persistence/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ const deleteServices = () => {
servicePreferences.clear();
};

const getAllServices = () => [
...localServices,
...nationalServices,
...specialServices
];

const getLocalServices = () => localServices.map(ls => ({ ...ls }));

const getPreference = (
Expand Down Expand Up @@ -158,6 +164,7 @@ const updatePreference = (
export default {
createServices,
deleteServices,
getAllServices,
getLocalServices,
getPreference,
getService,
Expand Down
91 changes: 55 additions & 36 deletions src/routers/service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Router } from "express";
import { pipe } from "fp-ts/lib/function";
import * as E from "fp-ts/lib/Either";
import * as O from "fp-ts/lib/Option";
import { ServiceId } from "../../generated/definitions/backend/ServiceId";
import { ServicePreference } from "../../generated/definitions/backend/ServicePreference";
import { UpsertServicePreference } from "../../generated/definitions/backend/UpsertServicePreference";
Expand Down Expand Up @@ -35,38 +37,60 @@ addHandler(
serviceRouter,
"get",
addApiV1Prefix("/services/:service_id"),
(req, res) => {
if (configResponse.getServiceResponseCode !== 200) {
res.sendStatus(configResponse.getServiceResponseCode);
return;
}
const serviceId = req.params.service_id as ServiceId;
const service = ServicesDB.getService(serviceId);
if (service === undefined) {
res.sendStatus(404);
return;
}
res.json(service);
}
(req, res) =>
pipe(
configResponse.getServiceResponseCode,
O.fromPredicate(statusCode => statusCode !== 200),
O.fold(
() =>
pipe(
req.params.service_id,
O.fromNullable,
O.chain(serviceId =>
pipe(
serviceId as ServiceId,
ServicesDB.getService,
O.fromNullable
)
),
O.fold(
() => res.sendStatus(404),
service => res.status(200).json(service)
)
),
statusCode => res.sendStatus(statusCode)
)
)
);

addHandler(
serviceRouter,
"get",
addApiV1Prefix("/services/:service_id/preferences"),
(req, res) => {
if (configResponse.getServicesPreference !== 200) {
res.sendStatus(configResponse.getServicesPreference);
return;
}
const serviceId = req.params.service_id as ServiceId;
const servicePreference = ServicesDB.getPreference(serviceId);
if (servicePreference === undefined) {
res.sendStatus(404);
return;
}
res.json(servicePreference);
}
(req, res) =>
pipe(
configResponse.getServicesPreference,
O.fromPredicate(statusCode => statusCode !== 200),
O.fold(
() =>
pipe(
req.params.service_id,
O.fromNullable,
O.chain(serviceId =>
pipe(
serviceId as ServiceId,
ServicesDB.getPreference,
O.fromNullable
)
),
O.fold(
() => res.sendStatus(404),
servicePreference => res.status(200).json(servicePreference)
)
),
statusCode => res.sendStatus(statusCode)
)
)
);

addHandler(
Expand Down Expand Up @@ -98,17 +122,12 @@ addHandler(
res.sendStatus(409);
return;
}
const increasedSettingsVersion =
((servicePreference.settings_version as number) +
1) as ServicePreference["settings_version"];
const updatedServicePreference = {

const persistedServicePreference = ServicesDB.updatePreference(serviceId, {
...updatedPreference,
settings_version: increasedSettingsVersion
} as ServicePreference;
const persistedServicePreference = ServicesDB.updatePreference(
serviceId,
updatedServicePreference
);
settings_version: servicePreference.settings_version + 1
} as ServicePreference);

if (!persistedServicePreference) {
res.sendStatus(500);
return;
Expand Down
4 changes: 3 additions & 1 deletion src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { satispayRouter } from "./routers/walletsV2/methods/satispay";
import { payPalRouter } from "./routers/walletsV3/methods/paypal";
import { delayer } from "./utils/delay_middleware";
import { walletRouter as newWalletRouter } from "./features/wallet";
import { serviceRouter as newServiceRouter } from "./features/services";
import { dashboardHomeRouter } from "./routers/configHomeDashboard/configHomeDashboard";

// create express server
Expand Down Expand Up @@ -80,7 +81,8 @@ app.use(fastLoginMiddleware);
idpayRouter,
lollipopRouter,
fastLoginRouter,
newWalletRouter
newWalletRouter,
newServiceRouter
].forEach(r => app.use(r));

export default app;
4 changes: 3 additions & 1 deletion src/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { PushNotificationsContentType } from "../../generated/definitions/backen
import { ReminderStatus } from "../../generated/definitions/backend/ReminderStatus";
import { MessagesConfig } from "../features/messages/types/messagesConfig";
import { WalletConfiguration } from "../features/wallet/types/configuration";
import { ServiceConfiguration } from "../features/services/types/configuration";
import { AllowRandomValue } from "./allowRandomValue";
import { HttpResponseCode } from "./httpResponseCode";

Expand Down Expand Up @@ -209,7 +210,8 @@ export const IoDevServerConfig = t.interface({
t.partial({
assertionRefValidityMS: t.number
})
])
]),
service: ServiceConfiguration
}),
t.partial({
wallet: WalletConfiguration
Expand Down

0 comments on commit 5621fca

Please sign in to comment.