Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Bonus Pagamenti Digitali): [#175096847] Support internal tests #2260

Merged
merged 8 commits into from
Oct 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ MYPORTAL_ENABLED=NO
BPD_ENABLED=NO
# enable playgrounds inside developer section
PLAYGROUNDS_ENABLED=NO
# it should be useless but it is required by BPD api
BPD_API_KEY=008e6ef2f8d04461835d67e86eae98ae
# endpoint BPD API
BPD_API_URL_PREFIX=https://bpd-dev.azure-api.net



4 changes: 4 additions & 0 deletions .env.local
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,9 @@ BPD_ENABLED=NO
MYPORTAL_ENABLED=NO
# enable playgrounds inside developer section
PLAYGROUNDS_ENABLED=NO
# it should be useless but it is required by BPD api
BPD_API_KEY=008e6ef2f8d04461835d67e86eae98ae
# endpoint BPD API
BPD_API_URL_PREFIX='http://127.0.0.1:3000/bonus'


4 changes: 4 additions & 0 deletions .env.production
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,7 @@ MYPORTAL_ENABLED=NO
BPD_ENABLED=NO
# enable playgrounds inside developer section
PLAYGROUNDS_ENABLED=NO
# it should be useless but it is required by BPD api
BPD_API_KEY=008e6ef2f8d04461835d67e86eae98ae
# endpoint BPD API
BPD_API_URL_PREFIX=https://bpd-dev.azure-api.net
4 changes: 4 additions & 0 deletions ts/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ export const myPortalEnabled: boolean = Config.MYPORTAL_ENABLED === "YES";

export const bpdEnabled: boolean = Config.BPD_ENABLED === "YES";

export const bpdApiKey: string = Config.BPD_API_KEY;

export const bpdApiUrlPrefix: string = Config.BPD_API_URL_PREFIX;

export const isPlaygroundsEnabled: boolean =
Config.PLAYGROUNDS_ENABLED === "YES";

Expand Down
63 changes: 51 additions & 12 deletions ts/features/bonus/bpd/api/backendBpdClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,23 @@ import {
ApiHeaderJson,
composeHeaderProducers,
createFetchRequestForApi,
MapResponseType,
RequestHeaderProducer
} from "italia-ts-commons/lib/requests";
import * as t from "io-ts";
import * as r from "italia-ts-commons/lib/requests";
import { defaultRetryingFetch } from "../../../../utils/fetch";
import {
enrollmentDefaultDecoder,
deleteUsingDELETEDefaultDecoder,
DeleteUsingDELETET,
enrollmentDecoder,
EnrollmentT,
findUsingGETDefaultDecoder,
findUsingGETDecoder,
FindUsingGETT
} from "../../../../../definitions/bpd/citizen/requestTypes";
import { Iban } from "../../../../../definitions/backend/Iban";
import { bpdApiKey } from "../../../../config";
import { PatchedCitizenResource } from "./patchedTypes";

const headersProducers = <
P extends {
Expand All @@ -29,21 +34,39 @@ const headersProducers = <
"Ocp-Apim-Subscription-Key" | "Content-Type" | "Authorization"
>;

const findT: FindUsingGETT = {
type FindUsingGETTExtra = MapResponseType<
FindUsingGETT,
200,
PatchedCitizenResource
>;
const findT: FindUsingGETTExtra = {
method: "get",
url: () => `/bonus/bpd/io/citizen`,
url: () => `/bpd/io/citizen`,
query: _ => ({}),
headers: headersProducers(),
response_decoder: findUsingGETDefaultDecoder()
response_decoder: findUsingGETDecoder(PatchedCitizenResource)
};

const enrollCitizenIOT: EnrollmentT = {
type EnrollmentTTExtra = MapResponseType<
EnrollmentT,
200,
PatchedCitizenResource
>;
const enrollCitizenIOT: EnrollmentTTExtra = {
method: "put",
url: () => `/bonus/bpd/io/citizen`,
url: () => `/bpd/io/citizen`,
query: _ => ({}),
body: _ => "",
headers: composeHeaderProducers(headersProducers(), ApiHeaderJson),
response_decoder: enrollmentDefaultDecoder()
response_decoder: enrollmentDecoder(PatchedCitizenResource)
};

const deleteCitizenIOT: DeleteUsingDELETET = {
method: "delete",
url: () => `/bpd/io/citizen`,
query: _ => ({}),
headers: headersProducers(),
response_decoder: deleteUsingDELETEDefaultDecoder()
};

// decoders composition to handle updatePaymentMethod response
Expand Down Expand Up @@ -85,7 +108,7 @@ const updatePaymentMethodT = (
): (() => Promise<t.Validation<finalType>>) => () =>
new Promise((res, rej) => {
options
.fetchApi(`${options.baseUrl}/bonus/bpd/io/citizen`, {
.fetchApi(`${options.baseUrl}/bpd/io/citizen`, {
method: "patch",
headers,
body: JSON.stringify(iban)
Expand All @@ -104,13 +127,13 @@ type Options = {
export function BackendBpdClient(
baseUrl: string,
token: string,
fiscalCode: string,
fetchApi: typeof fetch = defaultRetryingFetch()
) {
const options: Options = {
baseUrl,
fetchApi
};

// withBearerToken injects the header "Bearer" with token
// and "Ocp-Apim-Subscription-Key" with an hard-coded value (perhaps it won't be used)
type extendHeaders = {
Expand All @@ -119,16 +142,29 @@ export function BackendBpdClient(
readonly Bearer?: string;
["Ocp-Apim-Subscription-Key"]?: string;
};

// FIX ME !this code must be removed!
// only for test purpose
const withTestToken = () =>
fetchApi(
`https://bpd-dev.azure-api.net/bpd/pagopa/api/v1/login?fiscalCode=${fiscalCode}`,
{
method: "post"
}
);

const withBearerToken = <P extends extendHeaders, R>(
f: (p: P) => Promise<R>
) => async (po: P): Promise<R> => {
const params = Object.assign(buildHeaders(token), po) as P;
const reqTestToken = await withTestToken();
const testToken = await reqTestToken.text();
const params = Object.assign(buildHeaders(testToken), po) as P;
return f(params);
};

const buildHeaders = (token: string, content_type?: string) => {
const headers = {
"Ocp-Apim-Subscription-Key": "dummy_key",
"Ocp-Apim-Subscription-Key": bpdApiKey,
Bearer: token
};
return content_type
Expand All @@ -141,6 +177,9 @@ export function BackendBpdClient(
enrollCitizenIO: withBearerToken(
createFetchRequestForApi(enrollCitizenIOT, options)
),
deleteCitizenIO: withBearerToken(
createFetchRequestForApi(deleteCitizenIOT, options)
),
updatePaymentMethod: (iban: Iban) =>
updatePaymentMethodT(
options,
Expand Down
22 changes: 22 additions & 0 deletions ts/features/bonus/bpd/api/patchedTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as t from "io-ts";

// required attributes
const PatchedCitizenResourceR = t.interface({
enabled: t.boolean,

fiscalCode: t.string
});

// optional attributes
const PatchedCitizenResourceO = t.partial({
payoffInstr: t.string,

payoffInstrType: t.string
});

export const PatchedCitizenResource = t.intersection(
[PatchedCitizenResourceR, PatchedCitizenResourceO],
"PatchedCitizenResource"
);

export type PatchedCitizenResource = t.TypeOf<typeof PatchedCitizenResource>;
30 changes: 26 additions & 4 deletions ts/features/bonus/bpd/saga/index.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,39 @@
import { SagaIterator } from "redux-saga";
import { takeLatest } from "redux-saga/effects";
import * as pot from "italia-ts-commons/lib/pot";
import { takeLatest, select } from "redux-saga/effects";
import { getType } from "typesafe-actions";
import { apiUrlPrefix } from "../../../../config";
import { bpdApiUrlPrefix } from "../../../../config";
import { profileSelector } from "../../../../store/reducers/profile";
import { BackendBpdClient } from "../api/backendBpdClient";
import { bpdLoadActivationStatus } from "../store/actions/details";
import { bpdIbanInsertionStart, bpdUpsertIban } from "../store/actions/iban";
import {
bpdEnrollUserToProgram,
bpdDeleteUserFromProgram,
bpdOnboardingAcceptDeclaration,
bpdOnboardingStart
} from "../store/actions/onboarding";
import { getCitizen, putEnrollCitizen } from "./networking";
import { deleteCitizen, getCitizen, putEnrollCitizen } from "./networking";
import { patchCitizenIban } from "./networking/patchCitizenIban";
import { handleBpdIbanInsertion } from "./orchestration/insertIban";
import { handleBpdEnroll } from "./orchestration/onboarding/enrollToBpd";
import { handleBpdStartOnboardingSaga } from "./orchestration/onboarding/startOnboarding";

// watch all events about bpd
export function* watchBonusBpdSaga(bpdBearerToken: string): SagaIterator {
const bpdBackendClient = BackendBpdClient(apiUrlPrefix, bpdBearerToken);
const profileState: ReturnType<typeof profileSelector> = yield select(
profileSelector
);
const bpdBackendClient = BackendBpdClient(
bpdApiUrlPrefix,
bpdBearerToken,
// FIX ME !this code must be removed!
// only for test purpose
pot.getOrElse(
pot.map(profileState, p => p.fiscal_code as string),
""
)
);

// load citizen details
yield takeLatest(
Expand All @@ -34,6 +49,13 @@ export function* watchBonusBpdSaga(bpdBearerToken: string): SagaIterator {
bpdBackendClient.enrollCitizenIO
);

// delete citizen from the bpd
yield takeLatest(
bpdDeleteUserFromProgram.request,
deleteCitizen,
bpdBackendClient.deleteCitizenIO
);

// upsert citizen iban
yield takeLatest(
bpdUpsertIban.request,
Expand Down
29 changes: 28 additions & 1 deletion ts/features/bonus/bpd/saga/networking/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { SagaIterator } from "@redux-saga/core";
import { call, put } from "redux-saga/effects";
import { readableReport } from "italia-ts-commons/lib/reporters";
import { bpdEnrollUserToProgram } from "../../store/actions/onboarding";
import {
bpdDeleteUserFromProgram,
bpdEnrollUserToProgram
} from "../../store/actions/onboarding";
import { SagaCallReturnType } from "../../../../../types/utils";
import { BackendBpdClient } from "../../api/backendBpdClient";
import { bpdLoadActivationStatus } from "../../store/actions/details";
Expand Down Expand Up @@ -64,3 +67,27 @@ export function* putEnrollCitizen(
): SagaIterator {
yield call(executeAndDispatch, enrollCitizenIO, bpdEnrollUserToProgram);
}

/**
* make a request to delete citizen from bpd program
*/
export function* deleteCitizen(
deleteCitizenIO: ReturnType<typeof BackendBpdClient>["deleteCitizenIO"]
): SagaIterator {
try {
const deleteCitizenIOResult: SagaCallReturnType<typeof deleteCitizenIO> = yield call(
deleteCitizenIO,
{} as any
);
if (deleteCitizenIOResult.isRight()) {
if (deleteCitizenIOResult.value.status === 204) {
yield put(bpdDeleteUserFromProgram.success());
}
throw new Error(`response status ${deleteCitizenIOResult.value.status}`);
} else {
throw new Error(readableReport(deleteCitizenIOResult.value));
}
} catch (e) {
yield put(bpdDeleteUserFromProgram.failure(e));
}
}
12 changes: 11 additions & 1 deletion ts/features/bonus/bpd/store/actions/onboarding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ export const bpdEnrollUserToProgram = createAsyncAction(
"BPD_ENROLL_FAILURE"
)<void, BpdActivationPayload, Error>();

/**
* delete user from bpd program
*/
export const bpdDeleteUserFromProgram = createAsyncAction(
"BPD_DELETE_REQUEST",
"BPD_DELETE_SUCCESS",
"BPD_DELETE_FAILURE"
)<void, void, Error>();

/**
* Start the onboarding workflow
*/
Expand Down Expand Up @@ -48,4 +57,5 @@ export type BpdOnboardingActions =
| ActionType<typeof bpdOnboardingStart>
| ActionType<typeof bpdUserActivate>
| ActionType<typeof bpdOnboardingAcceptDeclaration>
| ActionType<typeof bpdOnboardingCancel>;
| ActionType<typeof bpdOnboardingCancel>
| ActionType<typeof bpdDeleteUserFromProgram>;
11 changes: 10 additions & 1 deletion ts/screens/profile/ProfileMainScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { AlertModal } from "../../components/ui/AlertModal";
import { LightModalContextInterface } from "../../components/ui/LightModal";
import Markdown from "../../components/ui/Markdown";
import Switch from "../../components/ui/Switch";
import { isPlaygroundsEnabled } from "../../config";
import { bpdEnabled, isPlaygroundsEnabled } from "../../config";
import I18n from "../../i18n";
import ROUTES from "../../navigation/routes";
import {
Expand Down Expand Up @@ -52,6 +52,7 @@ import { getAppVersion } from "../../utils/appVersion";
import { clipboardSetStringWithFeedback } from "../../utils/clipboard";
import { isDevEnv } from "../../utils/environment";
import { setStatusBarColorAndBackground } from "../../utils/statusBar";
import { bpdDeleteUserFromProgram } from "../../features/bonus/bpd/store/actions/onboarding";

type OwnProps = Readonly<{
navigation: NavigationScreenProp<NavigationState>;
Expand Down Expand Up @@ -483,6 +484,13 @@ class ProfileMainScreen extends React.PureComponent<Props, State> {
this.props.dispatchSessionExpired,
true
)}

{bpdEnabled &&
this.debugListItem(
"Leave BPD",
this.props.dispatchLeaveBpd,
true
)}
</React.Fragment>
)}
</React.Fragment>
Expand Down Expand Up @@ -548,6 +556,7 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({
preferencesPagoPaTestEnvironmentSetEnabled({ isPagoPATestEnabled })
),
dispatchSessionExpired: () => dispatch(sessionExpired()),
dispatchLeaveBpd: () => dispatch(bpdDeleteUserFromProgram.request()),
dispatchPreferencesExperimentalFeaturesSetEnabled: (enabled: boolean) =>
dispatch(preferencesExperimentalFeaturesSetEnabled(enabled))
});
Expand Down