Skip to content

Commit

Permalink
chore: [PE-510] Integration of LV for CGN flows (#5363)
Browse files Browse the repository at this point in the history
## Short description
This PR integrates LV to all CGN API requests;

## List of changes proposed in this pull request
- Added the `withRefreshApiCall` handler to all CGN API requests into
relative sagas
- Upgraded the CGN merchants definitions

## How to test
- Enable the login veloce feature on the dev-server and decrease the
session time.
- Open any CGN screen while the token is expired
- You should be able to see the session expired screen

---------

Co-authored-by: Fabio Bombardi <[email protected]>
  • Loading branch information
Hantex9 and shadowsheep1 authored Jan 15, 2024
1 parent 6260b35 commit febfc01
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 42 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"io_content_specs": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.29/definitions.yml",
"io_bonus_vacanze_specs": "https://raw.githubusercontent.com/pagopa/io-backend/v13.25.1-RELEASE/api_bonus.yaml",
"io_cgn_specs": "https://raw.githubusercontent.com/pagopa/io-backend/v13.25.1-RELEASE/api_cgn.yaml",
"io_cgn_merchants_specs": "https://raw.githubusercontent.com/pagopa/io-backend/v13.25.1-RELEASE/api_cgn_operator_search.yaml",
"io_cgn_merchants_specs": "https://raw.githubusercontent.com/pagopa/io-backend/v13.29.2-RELEASE/api_cgn_operator_search.yaml",
"io_bpd_citizen": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.29/bonus/specs/bpd/citizen.json",
"io_bpd_citizen_v2": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.29/bonus/specs/bpd/citizen_v2.json",
"io_bpd_payment": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.29/bonus/specs/bpd/payment.json",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ describe("handleGetEycaStatus", () => {
E.right({ status: 200, value: eycaCard })
);
it("With 200 should be FOUND and have an eyca card", () =>
expectSaga(handleGetEycaStatus, getEycaStatus)
expectSaga(handleGetEycaStatus, getEycaStatus, cgnEycaStatus.request())
.withReducer(appReducer)
.put(cgnEycaStatus.success({ status: "FOUND", card: eycaCard }))
.run()
Expand All @@ -70,7 +70,11 @@ describe("handleGetEycaStatus", () => {
it("With 404 should be NOT_FOUND", () => {
const getEycaStatus = jest.fn();
getEycaStatus.mockImplementation(() => E.right({ status: 404 }));
return expectSaga(handleGetEycaStatus, getEycaStatus)
return expectSaga(
handleGetEycaStatus,
getEycaStatus,
cgnEycaStatus.request()
)
.withReducer(appReducer)
.put(cgnEycaStatus.success({ status: "NOT_FOUND" }))
.run()
Expand All @@ -86,7 +90,11 @@ describe("handleGetEycaStatus", () => {
it("With 403 should be INELIGIBLE", () => {
const getEycaStatus = jest.fn();
getEycaStatus.mockImplementation(() => E.right({ status: 403 }));
return expectSaga(handleGetEycaStatus, getEycaStatus)
return expectSaga(
handleGetEycaStatus,
getEycaStatus,
cgnEycaStatus.request()
)
.withReducer(appReducer)
.put(cgnEycaStatus.success({ status: "INELIGIBLE" }))
.run()
Expand All @@ -102,7 +110,11 @@ describe("handleGetEycaStatus", () => {
it(`With 409 status should be ERROR`, () => {
const getEycaStatus = jest.fn();
getEycaStatus.mockImplementation(() => E.right({ status: 409 }));
return expectSaga(handleGetEycaStatus, getEycaStatus)
return expectSaga(
handleGetEycaStatus,
getEycaStatus,
cgnEycaStatus.request()
)
.withReducer(appReducer)
.put(cgnEycaStatus.success({ status: "ERROR" }))
.run()
Expand All @@ -120,7 +132,11 @@ describe("handleGetEycaStatus", () => {
const error = getGenericError(new Error(`response status ${status}`));
const getEycaStatus = jest.fn();
getEycaStatus.mockImplementation(() => E.right({ status }));
return expectSaga(handleGetEycaStatus, getEycaStatus)
return expectSaga(
handleGetEycaStatus,
getEycaStatus,
cgnEycaStatus.request()
)
.withReducer(appReducer)
.put(cgnEycaStatus.failure(error))
.run()
Expand All @@ -144,7 +160,11 @@ describe("handleGetEycaStatus", () => {
)
);
getEycaStatus.mockImplementation(() => error);
return expectSaga(handleGetEycaStatus, getEycaStatus)
return expectSaga(
handleGetEycaStatus,
getEycaStatus,
cgnEycaStatus.request()
)
.withReducer(appReducer)
.put(cgnEycaStatus.failure(genericError))
.run()
Expand Down
10 changes: 7 additions & 3 deletions ts/features/bonus/cgn/saga/networking/bucket/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getNetworkError } from "../../../../../../utils/errors";
import { readablePrivacyReport } from "../../../../../../utils/reporters";
import { BackendCgnMerchants } from "../../../api/backendCgnMerchants";
import { cgnCodeFromBucket } from "../../../store/actions/bucket";
import { withRefreshApiCall } from "../../../../../fastLogin/saga/utils";

// handle the request for CGN bucket consumption
export function* cgnBucketConsuption(
Expand All @@ -14,11 +15,14 @@ export function* cgnBucketConsuption(
cgnCodeFromBucketRequest: ReturnType<(typeof cgnCodeFromBucket)["request"]>
) {
try {
const discountBucketCodeResult: SagaCallReturnType<
typeof getDiscountBucketCode
> = yield* call(getDiscountBucketCode, {
const discountBacketRequest = getDiscountBucketCode({
discountId: cgnCodeFromBucketRequest.payload
});
const discountBucketCodeResult = (yield* call(
withRefreshApiCall,
discountBacketRequest,
cgnCodeFromBucketRequest
)) as unknown as SagaCallReturnType<typeof getDiscountBucketCode>;
if (E.isRight(discountBucketCodeResult)) {
if (discountBucketCodeResult.right.status === 200) {
yield* put(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { readableReport } from "@pagopa/ts-commons/lib/reporters";
import * as E from "fp-ts/lib/Either";
import { ActionType } from "typesafe-actions";
import { call, put } from "typed-redux-saga/macro";
import { PublishedProductCategories } from "../../../../../../../definitions/cgn/merchants/PublishedProductCategories";
import { PublishedProductCategoriesWithNewDiscountsCount } from "../../../../../../../definitions/cgn/merchants/PublishedProductCategoriesWithNewDiscountsCount";
Expand All @@ -10,6 +11,7 @@ import {
} from "../../../../../../utils/errors";
import { BackendCgnMerchants } from "../../../api/backendCgnMerchants";
import { cgnCategories } from "../../../store/actions/categories";
import { withRefreshApiCall } from "../../../../../fastLogin/saga/utils";

const checkIsCategoriesWithCount = (
cl:
Expand All @@ -22,12 +24,18 @@ const checkIsCategoriesWithCount = (
export function* cgnCategoriesSaga(
getPublishedCategories: ReturnType<
typeof BackendCgnMerchants
>["getPublishedCategories"]
>["getPublishedCategories"],
action: ActionType<(typeof cgnCategories)["request"]>
) {
try {
const publishedCategoriesResult: SagaCallReturnType<
typeof getPublishedCategories
> = yield* call(getPublishedCategories, { count_new_discounts: true });
const publishedCategoriesRequest = getPublishedCategories({
count_new_discounts: true
});
const publishedCategoriesResult = (yield* call(
withRefreshApiCall,
publishedCategoriesRequest,
action
)) as unknown as SagaCallReturnType<typeof getPublishedCategories>;
if (E.isLeft(publishedCategoriesResult)) {
yield* put(
cgnCategories.failure(
Expand All @@ -48,6 +56,9 @@ export function* cgnCategoriesSaga(
throw new Error(
`Expected a ProductCategoryWithNewDiscountsCount but received PublishedProductCategories`
);
}

if (publishedCategoriesResult.right.status === 401) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import { ActionType } from "typesafe-actions";
import { readableReport } from "@pagopa/ts-commons/lib/reporters";
import * as E from "fp-ts/lib/Either";
import { call, put } from "typed-redux-saga/macro";
import { SagaCallReturnType } from "../../../../../../types/utils";
import { getNetworkError } from "../../../../../../utils/errors";
import { BackendCGN } from "../../../api/backendCgn";
import { cgnDetails } from "../../../store/actions/details";
import { withRefreshApiCall } from "../../../../../fastLogin/saga/utils";

export function* cgnGetInformationSaga(
getCgnStatus: ReturnType<typeof BackendCGN>["getCgnStatus"]
getCgnStatus: ReturnType<typeof BackendCGN>["getCgnStatus"],
action: ActionType<(typeof cgnDetails)["request"]>
) {
try {
const cgnInformationResult: SagaCallReturnType<typeof getCgnStatus> =
yield* call(getCgnStatus, {});
const cgnInformationRequest = getCgnStatus({});
const cgnInformationResult = (yield* call(
withRefreshApiCall,
cgnInformationRequest,
action
)) as unknown as SagaCallReturnType<typeof getCgnStatus>;
if (E.isLeft(cgnInformationResult)) {
yield* put(
cgnDetails.failure({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
} from "../../../../../../../utils/errors";
import { StatusEnum } from "../../../../../../../../definitions/cgn/EycaActivationDetail";
import { cgnEycaActivation } from "../../../../store/actions/eyca/activation";
import { withRefreshApiCall } from "../../../../../../fastLogin/saga/utils";

// wait time between requests
const cgnResultPolling = 1000 as Millisecond;
Expand All @@ -37,9 +38,12 @@ export function* handleStartActivation(
startEycaActivation: ReturnType<typeof BackendCGN>["startEycaActivation"]
): Generator<ReduxSagaEffect, E.Either<NetworkError, StartEycaStatus>, any> {
try {
const startEycaActivationResult: SagaCallReturnType<
typeof startEycaActivation
> = yield* call(startEycaActivation, {});
const startEycaActivationRequest = startEycaActivation({});
const startEycaActivationResult = (yield* call(
withRefreshApiCall,
startEycaActivationRequest,
cgnEycaActivation.request()
)) as unknown as SagaCallReturnType<typeof startEycaActivation>;
if (E.isRight(startEycaActivationResult)) {
const status = startEycaActivationResult.right.status;
const activationStatus = mapStatus.get(status);
Expand All @@ -66,9 +70,12 @@ export function* getActivation(
getEycaActivation: ReturnType<typeof BackendCGN>["getEycaActivation"]
): Generator<ReduxSagaEffect, E.Either<NetworkError, GetEycaStatus>, any> {
try {
const getEycaActivationResult: SagaCallReturnType<
typeof getEycaActivation
> = yield* call(getEycaActivation, {});
const getEycaActivationRequest = getEycaActivation({});
const getEycaActivationResult = (yield* call(
withRefreshApiCall,
getEycaActivationRequest,
cgnEycaActivation.request()
)) as unknown as SagaCallReturnType<typeof getEycaActivation>;
if (E.isRight(getEycaActivationResult)) {
if (getEycaActivationResult.right.status === 200) {
const result = getEycaActivationResult.right.value;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ActionType } from "typesafe-actions";
import * as E from "fp-ts/lib/Either";
import { call, put } from "typed-redux-saga/macro";
import {
Expand All @@ -12,6 +13,7 @@ import { readablePrivacyReport } from "../../../../../../../utils/reporters";
import { BackendCGN } from "../../../../api/backendCgn";
import { cgnEycaStatus } from "../../../../store/actions/eyca/details";
import { EycaDetailKOStatus } from "../../../../store/reducers/eyca/details";
import { withRefreshApiCall } from "../../../../../../fastLogin/saga/utils";

const eycaStatusMap: Record<number, EycaDetailKOStatus> = {
403: "INELIGIBLE",
Expand All @@ -29,11 +31,16 @@ const eycaStatusMap: Record<number, EycaDetailKOStatus> = {
* @param getEycaStatus
*/
export function* handleGetEycaStatus(
getEycaStatus: ReturnType<typeof BackendCGN>["getEycaStatus"]
getEycaStatus: ReturnType<typeof BackendCGN>["getEycaStatus"],
getEycaStatusAction: ActionType<(typeof cgnEycaStatus)["request"]>
): Generator<ReduxSagaEffect, void, any> {
try {
const eycaInformationResult: SagaCallReturnType<typeof getEycaStatus> =
yield* call(getEycaStatus, {});
const eycaInformationRequest = getEycaStatus({});
const eycaInformationResult = (yield* call(
withRefreshApiCall,
eycaInformationRequest,
getEycaStatusAction
)) as unknown as SagaCallReturnType<typeof getEycaStatus>;
if (E.isLeft(eycaInformationResult)) {
yield* put(
cgnEycaStatus.failure(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
cgnOnlineMerchants,
cgnSelectedMerchant
} from "../../../store/actions/merchants";
import { withRefreshApiCall } from "../../../../../fastLogin/saga/utils";

export function* cgnOnlineMerchantsSaga(
getOnlineMerchants: ReturnType<
Expand All @@ -20,10 +21,14 @@ export function* cgnOnlineMerchantsSaga(
cgnOnlineMerchantRequest: ReturnType<typeof cgnOnlineMerchants.request>
) {
try {
const onlineMerchantsResult: SagaCallReturnType<typeof getOnlineMerchants> =
yield* call(getOnlineMerchants, {
body: cgnOnlineMerchantRequest.payload
});
const onlineMerchantsRequest = getOnlineMerchants({
body: cgnOnlineMerchantRequest.payload
});
const onlineMerchantsResult = (yield* call(
withRefreshApiCall,
onlineMerchantsRequest,
cgnOnlineMerchantRequest
)) as unknown as SagaCallReturnType<typeof getOnlineMerchants>;

if (E.isLeft(onlineMerchantsResult)) {
yield* put(
Expand All @@ -40,6 +45,9 @@ export function* cgnOnlineMerchantsSaga(
);
return;
}
if (onlineMerchantsResult.right.status === 401) {
return;
}

throw new Error(`Response in status ${onlineMerchantsResult.right.status}`);
} catch (e) {
Expand All @@ -54,11 +62,14 @@ export function* cgnOfflineMerchantsSaga(
cgnOfflineMerchantRequest: ReturnType<typeof cgnOfflineMerchants.request>
) {
try {
const offlineMerchantsResult: SagaCallReturnType<
typeof getOfflineMerchants
> = yield* call(getOfflineMerchants, {
const offlineMerchantRequest = getOfflineMerchants({
body: cgnOfflineMerchantRequest.payload
});
const offlineMerchantsResult = (yield* call(
withRefreshApiCall,
offlineMerchantRequest,
cgnOfflineMerchantRequest
)) as unknown as SagaCallReturnType<typeof getOfflineMerchants>;

if (E.isLeft(offlineMerchantsResult)) {
yield* put(
Expand All @@ -77,6 +88,9 @@ export function* cgnOfflineMerchantsSaga(
);
return;
}
if (offlineMerchantsResult.right.status === 401) {
return;
}

throw new Error(
`Response in status ${offlineMerchantsResult.right.status}`
Expand All @@ -91,8 +105,14 @@ export function* cgnMerchantDetail(
merchantSelected: ReturnType<(typeof cgnSelectedMerchant)["request"]>
) {
try {
const merchantDetailResult: SagaCallReturnType<typeof getMerchant> =
yield* call(getMerchant, { merchantId: merchantSelected.payload });
const merchantDetailRequest = getMerchant({
merchantId: merchantSelected.payload
});
const merchantDetailResult = (yield* call(
withRefreshApiCall,
merchantDetailRequest,
merchantSelected
)) as unknown as SagaCallReturnType<typeof getMerchant>;
if (E.isLeft(merchantDetailResult)) {
yield* put(
cgnSelectedMerchant.failure(
Expand All @@ -107,6 +127,10 @@ export function* cgnMerchantDetail(
return;
}

if (merchantDetailResult.right.status === 401) {
return;
}

throw new Error(`Response in status ${merchantDetailResult.right.status}`);
} catch (e) {
yield* put(cgnSelectedMerchant.failure(getNetworkError(e)));
Expand Down
13 changes: 10 additions & 3 deletions ts/features/bonus/cgn/saga/networking/otp/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import { ActionType } from "typesafe-actions";
import * as E from "fp-ts/lib/Either";
import { call, put } from "typed-redux-saga/macro";
import { SagaCallReturnType } from "../../../../../../types/utils";
import { getNetworkError } from "../../../../../../utils/errors";
import { readablePrivacyReport } from "../../../../../../utils/reporters";
import { BackendCGN } from "../../../api/backendCgn";
import { cgnGenerateOtp as cgnGenerateOtpAction } from "../../../store/actions/otp";
import { withRefreshApiCall } from "../../../../../fastLogin/saga/utils";

// handle the request for CGN Otp code generation
export function* cgnGenerateOtp(
generateOtp: ReturnType<typeof BackendCGN>["generateOtp"]
generateOtp: ReturnType<typeof BackendCGN>["generateOtp"],
action: ActionType<(typeof cgnGenerateOtpAction)["request"]>
) {
try {
const generateOtpResult: SagaCallReturnType<typeof generateOtp> =
yield* call(generateOtp, {});
const generateOtpRequest = generateOtp({});
const generateOtpResult = (yield* call(
withRefreshApiCall,
generateOtpRequest,
action
)) as unknown as SagaCallReturnType<typeof generateOtp>;
if (E.isRight(generateOtpResult)) {
if (generateOtpResult.right.status === 200) {
yield* put(cgnGenerateOtpAction.success(generateOtpResult.right.value));
Expand Down
Loading

0 comments on commit febfc01

Please sign in to comment.