Skip to content

Commit 5771b67

Browse files
authored
feat: [IOCOM-2939] SEND lollipopLambda implementation (#572)
* SEND lollipopLambda implementation * io-services-metadata version and config
1 parent 3aa6ce4 commit 5771b67

File tree

9 files changed

+247
-11
lines changed

9 files changed

+247
-11
lines changed

scripts/generate-api-models.sh

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
IO_BACKEND_VERSION=v17.5.2
44
# need to change after merge on io-services-metadata
5-
IO_SERVICES_METADATA_VERSION=1.0.93
5+
IO_SERVICES_METADATA_VERSION=1.0.95
66
# Session manager version
77
IO_SESSION_MANAGER_VERSION=1.8.0
8+
# Send Functions
9+
IO_SEND_FUNC=1.5.5
810

911
declare -a noParams=(
1012
"./generated/definitions/backend https://raw.githubusercontent.com/pagopa/io-backend/$IO_BACKEND_VERSION/api_public.yaml"
@@ -31,7 +33,8 @@ declare -a noStrict=(
3133
)
3234

3335
declare -a noStrictRequestTypesRespondeDecoders=(
34-
"./generated/definitions/pn/aar https://raw.githubusercontent.com/pagopa/io-messages/send-func@1.4.1/apps/send-func/openapi/aar-notification.yaml"
36+
"./generated/definitions/pn/aar https://raw.githubusercontent.com/pagopa/io-messages/send-func@$IO_SEND_FUNC/apps/send-func/openapi/aar-notification.yaml"
37+
"./generated/definitions/pn/lollipopLambda https://raw.githubusercontent.com/pagopa/io-messages/send-func@$IO_SEND_FUNC/apps/send-func/openapi/lollipop-integration-check.yaml"
3538
"./generated/definitions/pn https://raw.githubusercontent.com/pagopa/io-backend/$IO_BACKEND_VERSION/api_pn.yaml"
3639
"./generated/definitions/trial_system https://raw.githubusercontent.com/pagopa/io-backend/$IO_BACKEND_VERSION/api_trial_system.yaml"
3740
"./generated/definitions/fims_history https://raw.githubusercontent.com/pagopa/io-backend/$IO_BACKEND_VERSION/api_io_fims.yaml"

src/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ const defaultConfig: IoDevServerConfig = {
120120
send: {
121121
isServiceUpsertRateLimited: false,
122122
aarQRCodeUrl: "https://cittadini.notifichedigitali.it/io",
123+
lollipopLambdaResponseCode: 200,
123124
mandateTimeToLiveSeconds: 120,
124125
paymentDocumentExpirationTimeSeconds: 10,
125126
paymentDocumentGenerationTimeSeconds: 3,

src/features/messages/routers/ioSendRouter.ts

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
} from "../services/ioSendService";
2222
import MessagesService from "../services/messagesService";
2323
import { bodyToString } from "../utils";
24+
import { generateLollipopLambdaGetPath } from "../../pn/routers/lollipopLambda";
2425

2526
export const ioSendRouter = Router();
2627

@@ -163,21 +164,68 @@ addHandler(
163164
() => Math.random() * 500
164165
);
165166

167+
addHandler(
168+
ioSendRouter,
169+
"get",
170+
"/api/com/v1/send/lollipop-check/test",
171+
lollipopMiddleware(async (req, res) => {
172+
const sendLollipopLambdaGetUrl = `${serverUrl}${generateLollipopLambdaGetPath()}`;
173+
commonHandleIsTestQueryParam(req);
174+
const sendLollipopLambdaGetFetch = () =>
175+
fetch(sendLollipopLambdaGetUrl, {
176+
method: "get",
177+
headers: generateRequestHeaders(req.headers, "application/json", true)
178+
});
179+
await fetchSENDDataAndForwardResponse(
180+
sendLollipopLambdaGetFetch,
181+
"lollipop-test",
182+
res
183+
);
184+
}),
185+
() => Math.random() * 500
186+
);
187+
188+
addHandler(
189+
ioSendRouter,
190+
"post",
191+
"/api/com/v1/send/lollipop-check/test",
192+
lollipopMiddleware(async (req, res) => {
193+
const sendLollipopLambdaPostUrl = `${serverUrl}${generateLollipopLambdaGetPath()}`;
194+
const sendLollipopLambdaPostBodyEither = bodyToString(req.body);
195+
if (handleLeftEitherIfNeeded(sendLollipopLambdaPostBodyEither, res)) {
196+
return;
197+
}
198+
commonHandleIsTestQueryParam(req);
199+
const sendLambdaLollipopPostFetch = () =>
200+
fetch(sendLollipopLambdaPostUrl, {
201+
method: "post",
202+
headers: generateRequestHeaders(req.headers, "application/json", true),
203+
body: sendLollipopLambdaPostBodyEither.right
204+
});
205+
await fetchSENDDataAndForwardResponse(
206+
sendLambdaLollipopPostFetch,
207+
"lollipop-test",
208+
res
209+
);
210+
}),
211+
() => Math.random() * 500
212+
);
213+
166214
const fetchSENDDataAndForwardResponse = async (
167215
fetchFunction: () => Promise<globalThis.Response>,
168216
endpointName: string,
169217
res: Response
170218
) => {
171219
try {
172-
const sendQResponse = await fetchFunction();
220+
const sendResponse = await fetchFunction();
173221

174-
const contentType = sendQResponse.headers.get("content-type");
175-
const responseBodyBuffer = await sendQResponse.arrayBuffer();
222+
const contentType = sendResponse.headers.get("content-type");
223+
const responseBodyBuffer = await sendResponse.arrayBuffer();
176224
const body = Buffer.from(responseBodyBuffer);
177225
if (contentType) {
178226
res.setHeader("Content-Type", contentType);
179227
}
180-
res.status(sendQResponse.status).send(body);
228+
res.status(sendResponse.status).send(body);
181229
} catch (e) {
182230
const errorMessage = unknownToString(e);
183231
res

src/features/messages/services/ioSendService.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,19 @@ export const isTestOrUndefinedFromQuery = (
2929

3030
export const generateRequestHeaders = (
3131
headers: IncomingHttpHeaders,
32-
contentType: string = "application/json"
32+
contentType: string = "application/json",
33+
excludeTaxId: boolean = false
3334
): Record<string, string> => ({
3435
...MessagesService.lollipopClientHeadersFromHeaders(headers),
3536
...MessagesService.generateFakeLollipopServerHeaders(
3637
ioDevServerConfig.profile.attrs.fiscal_code
3738
),
3839
...MessagesService.sendAPIKeyHeader(),
39-
...MessagesService.sendTaxIdHeader(
40-
ioDevServerConfig.profile.attrs.fiscal_code
41-
),
40+
...(excludeTaxId
41+
? {}
42+
: MessagesService.sendTaxIdHeader(
43+
ioDevServerConfig.profile.attrs.fiscal_code
44+
)),
4245
"Content-Type": contentTypeHeaderFromHeaders(headers, contentType),
4346
...ioSourceHeaderFromRequestHeaders(headers)
4447
});
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { Request, Response, Router } from "express";
2+
import { addHandler } from "../../../payloads/response";
3+
import { initializationMiddleware } from "../middlewares/initializationMiddleware";
4+
import { authenticationMiddleware } from "../middlewares/authenticationMiddleware";
5+
import { ioDevServerConfig } from "../../../config";
6+
import { handleLeftEitherIfNeeded } from "../../../utils/error";
7+
import { checkAndValidateLollipopHeaders } from "../services/lollipopService";
8+
import { lollipopLambdaResponseFromBodyAndConfig } from "../services/lollipopLambdaService";
9+
10+
const lollipopLambdaPath = "/aws/send/lollipop-test";
11+
12+
export const generateLollipopLambdaGetPath = () => lollipopLambdaPath;
13+
export const generateLollipopLambdaPostPath = () => lollipopLambdaPath;
14+
15+
export const sendLollipopLambdaRouter = Router();
16+
17+
addHandler(
18+
sendLollipopLambdaRouter,
19+
"get",
20+
lollipopLambdaPath,
21+
// Middleware have to be used like this (instead of directly giving the middleware to the router via use)
22+
// because supertest (when testing) calls every middleware upon test initialization, even if it not in a
23+
// router directly called by the test, thus making every test fail due to the authentication middleware
24+
authenticationMiddleware(
25+
initializationMiddleware((req, res) =>
26+
commonLollipopLambdaHandling("GET", req, res)
27+
)
28+
)
29+
);
30+
31+
addHandler(
32+
sendLollipopLambdaRouter,
33+
"post",
34+
lollipopLambdaPath,
35+
// Middleware have to be used like this (instead of directly giving the middleware to the router via use)
36+
// because supertest (when testing) calls every middleware upon test initialization, even if it not in a
37+
// router directly called by the test, thus making every test fail due to the authentication middleware
38+
authenticationMiddleware(
39+
initializationMiddleware((req, res) =>
40+
commonLollipopLambdaHandling("POST", req, res)
41+
)
42+
)
43+
);
44+
45+
const commonLollipopLambdaHandling = (
46+
requestVerb: "GET" | "POST",
47+
req: Request,
48+
res: Response
49+
) => {
50+
const sendConfig = ioDevServerConfig.send;
51+
if (!sendConfig.skipLollipopVerification) {
52+
const lollipopHeadersEither = checkAndValidateLollipopHeaders(req.headers);
53+
if (handleLeftEitherIfNeeded(lollipopHeadersEither, res)) {
54+
return;
55+
}
56+
}
57+
58+
const requestBody = requestVerb === "POST" ? req.body : undefined;
59+
const lollipopLambdaGetResponseEither =
60+
lollipopLambdaResponseFromBodyAndConfig(
61+
requestVerb,
62+
requestBody,
63+
sendConfig
64+
);
65+
if (handleLeftEitherIfNeeded(lollipopLambdaGetResponseEither, res)) {
66+
return;
67+
}
68+
res.status(200).json(lollipopLambdaGetResponseEither.right);
69+
};
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { Either, isLeft, left } from "fp-ts/lib/Either";
2+
import { readableReportSimplified } from "@pagopa/ts-commons/lib/reporters";
3+
import { SendConfig } from "../types/sendConfig";
4+
import { ExpressFailure } from "../../../utils/expressDTO";
5+
import { SuccessResponse } from "../../../../generated/definitions/pn/lollipopLambda/SuccessResponse";
6+
import { getProblemJson } from "../../../payloads/error";
7+
import { ErrorResponse } from "../../../../generated/definitions/pn/lollipopLambda/ErrorResponse";
8+
9+
export const lollipopLambdaResponseFromBodyAndConfig = (
10+
requestVerb: "GET" | "POST",
11+
requestBody: unknown,
12+
sendConfig: SendConfig
13+
): Either<ExpressFailure, SuccessResponse> => {
14+
const timestamp = new Date();
15+
const statusCode = sendConfig.lollipopLambdaResponseCode ?? 200;
16+
if (statusCode === 401) {
17+
return left({
18+
httpStatusCode: 401,
19+
reason: {}
20+
});
21+
} else if (statusCode === 400 || statusCode === 403 || statusCode === 500) {
22+
const errorResponseEither = ErrorResponse.decode({
23+
success: false,
24+
timestamp,
25+
error: {
26+
message: `Server was configured to send a ${statusCode} status code`,
27+
statusCode
28+
}
29+
});
30+
if (isLeft(errorResponseEither)) {
31+
return left({
32+
httpStatusCode: 500,
33+
reason: getProblemJson(
34+
500,
35+
"Conversion to ErrorResponse failed",
36+
`Unable to convert input data to the ErrorResponse data structure (${readableReportSimplified(
37+
errorResponseEither.left
38+
)})`
39+
)
40+
});
41+
}
42+
return left({
43+
httpStatusCode: statusCode,
44+
reason: errorResponseEither.right
45+
});
46+
}
47+
const bodyStringOrUndefined = unknownBodyToStringOrUndefined(requestBody);
48+
const bodyLengthOrUndefined = stringOrUndefinedToByteLengthOrUndefined(
49+
bodyStringOrUndefined
50+
);
51+
const successResponseEither = SuccessResponse.decode({
52+
success: true,
53+
timestamp,
54+
data: {
55+
message: "Server was configured to send a 200 status code",
56+
timestamp,
57+
request: {
58+
method: requestVerb,
59+
path: "lollipop-test",
60+
hasBody: !!bodyLengthOrUndefined,
61+
bodyLength: bodyLengthOrUndefined ?? 0
62+
}
63+
}
64+
});
65+
if (isLeft(successResponseEither)) {
66+
return left({
67+
httpStatusCode: 500,
68+
reason: getProblemJson(
69+
500,
70+
"Conversion to SuccessResponse failed",
71+
`Unable to convert input data to the SuccessResponse data structure (${readableReportSimplified(
72+
successResponseEither.left
73+
)})`
74+
)
75+
});
76+
}
77+
return successResponseEither;
78+
};
79+
80+
const stringOrUndefinedToByteLengthOrUndefined = (
81+
input: string | undefined
82+
): number | undefined => (input ? Buffer.byteLength(input, "utf8") : undefined);
83+
84+
const unknownBodyToStringOrUndefined = (body: unknown): string | undefined => {
85+
if (typeof body === "string") {
86+
return body;
87+
} else if (typeof body === "object") {
88+
try {
89+
return JSON.stringify(body);
90+
} catch {
91+
return undefined;
92+
}
93+
} else if (
94+
typeof body === "number" ||
95+
(typeof BigInt !== "undefined" && typeof body === "bigint") ||
96+
typeof body === "boolean" ||
97+
typeof body === "symbol"
98+
) {
99+
return String(body);
100+
}
101+
return undefined;
102+
};

src/features/pn/types/sendConfig.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ export const SendConfig = t.intersection([
7171
}),
7272
t.partial({
7373
aarQRCodeUrl: t.string,
74+
lollipopLambdaResponseCode: t.union([
75+
t.literal(200),
76+
t.literal(400),
77+
t.literal(401),
78+
t.literal(403),
79+
t.literal(500)
80+
]),
7481
mandateTimeToLiveSeconds: t.number,
7582
paymentDocumentExpirationTimeSeconds: t.number,
7683
paymentDocumentGenerationTimeSeconds: t.number,

src/payloads/backend.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,8 @@ export const backendStatus: BackendStatus = {
156156
customerServiceCenterUrl: "https://assistenza.notifichedigitali.it/hc",
157157
estimateTimelinesUrl: "https://notifichedigitali.it/perfezionamento",
158158
visitTheSENDWebsiteUrl:
159-
"https://cittadini.notifichedigitali.it/auth/login"
159+
"https://cittadini.notifichedigitali.it/auth/login",
160+
lollipopPlaygroundEnabled: true
160161
},
161162
idPay: {
162163
min_app_version: {

src/server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import { sendServiceRouter } from "./features/pn/routers/serviceRouter";
4444
import { sendMandatesRouter } from "./features/pn/routers/mandatesRouter";
4545
import { ioSendRouter } from "./features/messages/routers/ioSendRouter";
4646
import { cdcRouter } from "./features/cdc/routers";
47+
import { sendLollipopLambdaRouter } from "./features/pn/routers/lollipopLambda";
4748

4849
// create express server
4950
const app: Application = express();
@@ -87,6 +88,7 @@ app.use(fastLoginMiddleware);
8788
fciRouter,
8889
sendAARRouter,
8990
sendDocumentsRouter,
91+
sendLollipopLambdaRouter,
9092
sendMandatesRouter,
9193
sendNotificationsRouter,
9294
sendPrevalidatedUrisRouter,

0 commit comments

Comments
 (0)