Skip to content

Commit

Permalink
feat(Carta Giovani Nazionale): [#176959318,#177244438,#177244411] Add…
Browse files Browse the repository at this point in the history
… OTP generation network / actions / saga / reducer (#2882)

* [#176959318] add otp generation api / actions / saga / reducer / test

* [#176959318] update comments

* [#176959318] restore

* [#176959318] refactoring

* [#176959318] fix name conlficts

* [#176959318] fix name conlficts

Co-authored-by: Cristiano Tofani <[email protected]>
  • Loading branch information
Undermaken and CrisTofani authored Mar 10, 2021
1 parent f856dba commit 04c9905
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 4 deletions.
14 changes: 13 additions & 1 deletion ts/features/bonus/cgn/api/backendCgn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
import { Omit } from "italia-ts-commons/lib/types";
import { defaultRetryingFetch } from "../../../../utils/fetch";
import {
generateOtpDefaultDecoder,
GenerateOtpT,
getCgnActivationDefaultDecoder,
GetCgnActivationT,
getCgnStatusDefaultDecoder,
Expand Down Expand Up @@ -43,6 +45,15 @@ const getCgnStatus: GetCgnStatusT = {
response_decoder: getCgnStatusDefaultDecoder()
};

const generateOtp: GenerateOtpT = {
method: "post",
url: () => `/api/v1/cgn/otp`,
query: _ => ({}),
body: () => "",
headers: composeHeaderProducers(tokenHeaderProducer, ApiHeaderJson),
response_decoder: generateOtpDefaultDecoder()
};

function ParamAuthorizationBearerHeaderProducer<
P extends { readonly Bearer: string }
>(): RequestHeaderProducer<P, "Authorization"> {
Expand Down Expand Up @@ -82,6 +93,7 @@ export function BackendCGN(
),
getCgnStatus: withBearerToken(
createFetchRequestForApi(getCgnStatus, options)
)
),
generateOtp: withBearerToken(createFetchRequestForApi(generateOtp, options))
};
}
9 changes: 9 additions & 0 deletions ts/features/bonus/cgn/saga/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import {
import { apiUrlPrefix } from "../../../../config";
import { BackendCGN } from "../api/backendCgn";
import { cgnDetails } from "../store/actions/details";
import { cgnGenerateOtp as cgnGenerateOtpAction } from "../store/actions/otp";
import { handleCgnStartActivationSaga } from "./orchestration/activation/activationSaga";
import { handleCgnActivationSaga } from "./orchestration/activation/handleActivationSaga";
import {
cgnActivationSaga,
handleCgnStatusPolling
} from "./networking/activation/getBonusActivationSaga";
import { cgnGetInformationSaga } from "./networking/details/getCgnInformationSaga";
import { cgnGenerateOtp } from "./networking/otp";

export function* watchBonusCgnSaga(bearerToken: string): SagaIterator {
// create client to exchange data with the APIs
Expand All @@ -39,4 +41,11 @@ export function* watchBonusCgnSaga(bearerToken: string): SagaIterator {
cgnGetInformationSaga,
backendCGN.getCgnStatus
);

// CGN Otp generation
yield takeLatest(
getType(cgnGenerateOtpAction.request),
cgnGenerateOtp,
backendCGN.generateOtp
);
}
41 changes: 41 additions & 0 deletions ts/features/bonus/cgn/saga/networking/otp/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { call, put } from "redux-saga/effects";
import { BackendCGN } from "../../../api/backendCgn";
import { SagaCallReturnType } from "../../../../../../types/utils";
import { getNetworkError } from "../../../../../../utils/errors";
import { cgnGenerateOtp as cgnGenerateOtpAction } from "../../../store/actions/otp";
import { readablePrivacyReport } from "../../../../../../utils/reporters";

// handle the request for CGN Otp code generation
export function* cgnGenerateOtp(
generateOtp: ReturnType<typeof BackendCGN>["generateOtp"]
) {
try {
const generateOtpResult: SagaCallReturnType<typeof generateOtp> = yield call(
generateOtp,
{}
);
if (generateOtpResult.isRight()) {
if (generateOtpResult.value.status === 200) {
yield put(cgnGenerateOtpAction.success(generateOtpResult.value.value));
} else {
yield put(
cgnGenerateOtpAction.failure(
getNetworkError(
new Error(`response status ${generateOtpResult.value.status}`)
)
)
);
}
} else {
yield put(
cgnGenerateOtpAction.failure(
getNetworkError(
new Error(readablePrivacyReport(generateOtpResult.value))
)
)
);
}
} catch (e) {
yield put(cgnGenerateOtpAction.failure(getNetworkError(e)));
}
}
4 changes: 3 additions & 1 deletion ts/features/bonus/cgn/store/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { CgnDetailsActions } from "./details";
import { CgnEycaActivationActions } from "./eyca/activation";
import { CgnEycaDetailsActions } from "./eyca/details";
import { CgnMerchantsAction } from "./merchants";
import { CgnOtpActions } from "./otp";

export type CgnActions =
| CgnActivationActions
| CgnDetailsActions
| CgnEycaActivationActions
| CgnEycaDetailsActions
| CgnMerchantsAction;
| CgnMerchantsAction
| CgnOtpActions;
14 changes: 14 additions & 0 deletions ts/features/bonus/cgn/store/actions/otp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ActionType, createAsyncAction } from "typesafe-actions";
import { NetworkError } from "../../../../../utils/errors";
import { Otp } from "../../../../../../definitions/cgn/Otp";

/**
* handle CGN Otp generation
*/
export const cgnGenerateOtp = createAsyncAction(
"CGN_GENERATE_OTP_REQUEST",
"CGN_GENERATE_OTP_SUCCESS",
"CGN_GENERATE_OTP_FAILURE"
)<void, Otp, NetworkError>();

export type CgnOtpActions = ActionType<typeof cgnGenerateOtp>;
47 changes: 47 additions & 0 deletions ts/features/bonus/cgn/store/reducers/__test__/otp.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { GlobalState } from "../../../../../../store/reducers/types";
import { appReducer } from "../../../../../../store/reducers";
import {
isReady,
remoteError,
remoteLoading,
remoteReady
} from "../../../../bpd/model/RemoteValue";
import { cgnGenerateOtp } from "../../actions/otp";
import { cgnOtpDataSelector } from "../otp";
import { OtpCode } from "../../../../../../../definitions/cgn/OtpCode";
import { getGenericError } from "../../../../../../utils/errors";

describe("cgnOtpReducer", () => {
it("should be loading", () => {
const globalState: GlobalState = appReducer(
undefined,
cgnGenerateOtp.request()
);
expect(cgnOtpDataSelector(globalState)).toEqual(remoteLoading);
});

it("should be ready", () => {
const otpData = {
code: "M6R3E4PNG36" as OtpCode,
expires_at: new Date("2021-09-08T00:53:12.966Z"),
ttl: 100
};
const globalState: GlobalState = appReducer(
undefined,
cgnGenerateOtp.success(otpData)
);
expect(isReady(cgnOtpDataSelector(globalState))).toBeTruthy();
if (isReady(cgnOtpDataSelector(globalState))) {
expect(cgnOtpDataSelector(globalState)).toEqual(remoteReady(otpData));
}
});

it("should be error", () => {
const genericError = getGenericError(new Error("an error"));
const globalState: GlobalState = appReducer(
undefined,
cgnGenerateOtp.failure(genericError)
);
expect(cgnOtpDataSelector(globalState)).toEqual(remoteError(genericError));
});
});
7 changes: 5 additions & 2 deletions ts/features/bonus/cgn/store/reducers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@ import cgnActivationReducer, { ActivationState } from "./activation";
import cgnDetailsReducer, { CgnDetailsState } from "./details";
import eycaReducer, { EycaState } from "./eyca";
import cgnMerchantsReducer, { CgnMerchantsState } from "./merchants";
import cgnOtpReducer, { CgnOtpState } from "./otp";

export type CgnState = {
activation: ActivationState;
detail: CgnDetailsState;
eyca: EycaState;
merchants: CgnMerchantsState;
otp: CgnOtpState;
};

const cgnReducer = combineReducers<CgnState, Action>({
activation: cgnActivationReducer,
detail: cgnDetailsReducer,
eyca: eycaReducer,
merchants: cgnMerchantsReducer
merchants: cgnMerchantsReducer,
otp: cgnOtpReducer,
eyca: eycaReducer
});

export default cgnReducer;
50 changes: 50 additions & 0 deletions ts/features/bonus/cgn/store/reducers/otp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { getType } from "typesafe-actions";
import { Action } from "../../../../../store/actions/types";
import { GlobalState } from "../../../../../store/reducers/types";
import { NetworkError } from "../../../../../utils/errors";
import { Otp } from "../../../../../../definitions/cgn/Otp";
import { cgnGenerateOtp } from "../actions/otp";
import {
remoteError,
remoteLoading,
remoteReady,
remoteUndefined,
RemoteValue
} from "../../../bpd/model/RemoteValue";

export type CgnOtpState = {
data: RemoteValue<Otp, NetworkError>;
};

const INITIAL_STATE: CgnOtpState = {
data: remoteUndefined
};

const reducer = (
state: CgnOtpState = INITIAL_STATE,
action: Action
): CgnOtpState => {
switch (action.type) {
case getType(cgnGenerateOtp.request):
return {
...state,
data: remoteLoading
};
case getType(cgnGenerateOtp.success):
return {
...state,
data: remoteReady(action.payload)
};
case getType(cgnGenerateOtp.failure):
return {
...state,
data: remoteError(action.payload)
};
}
return state;
};

export default reducer;

export const cgnOtpDataSelector = (state: GlobalState): CgnOtpState["data"] =>
state.bonus.cgn.otp.data;

0 comments on commit 04c9905

Please sign in to comment.