Skip to content

Commit

Permalink
Merge branch 'master' into 176245381-ko-search-cobadge-screens
Browse files Browse the repository at this point in the history
  • Loading branch information
fabriziofff authored Feb 10, 2021
2 parents 88f8ff4 + 048b0e2 commit acbfabe
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 24 deletions.
95 changes: 95 additions & 0 deletions ts/features/wallet/onboarding/cobadge/analytics/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { getType } from "typesafe-actions";
import { CoBadgeServices } from "../../../../../../definitions/pagopa/cobadge/configuration/CoBadgeServices";
import { CobadgeResponse } from "../../../../../../definitions/pagopa/walletv2/CobadgeResponse";
import { ExecutionStatusEnum } from "../../../../../../definitions/pagopa/walletv2/SearchRequestMetadata";
import { mixpanel } from "../../../../../mixpanel";
import { Action } from "../../../../../store/actions/types";
import { getNetworkErrorMessage } from "../../../../../utils/errors";
import {
addCoBadgeToWallet,
loadCoBadgeAbiConfiguration,
searchUserCoBadge,
walletAddCoBadgeBack,
walletAddCoBadgeCancel,
walletAddCoBadgeCompleted,
walletAddCoBadgeStart
} from "../store/actions";

export const trackCoBadgeAction = (mp: NonNullable<typeof mixpanel>) => (
action: Action
): Promise<any> => {
switch (action.type) {
case getType(walletAddCoBadgeStart):
return mp.track(action.type, {
abi: action.payload
});
case getType(walletAddCoBadgeCompleted):
case getType(walletAddCoBadgeCancel):
case getType(walletAddCoBadgeBack):
case getType(addCoBadgeToWallet.request):
case getType(loadCoBadgeAbiConfiguration.request):
return mp.track(action.type);
case getType(searchUserCoBadge.request):
return mp.track(action.type, { abi: action.payload ?? "all" });
case getType(loadCoBadgeAbiConfiguration.success):
return mp.track(action.type, trackCoBadgeServices(action.payload));
case getType(searchUserCoBadge.success):
return mp.track(action.type, trackCobadgeResponse(action.payload));
case getType(addCoBadgeToWallet.success):
return mp.track(action.type, {
abi: action.payload.info.issuerAbiCode
});
case getType(addCoBadgeToWallet.failure):
case getType(loadCoBadgeAbiConfiguration.failure):
case getType(searchUserCoBadge.failure):
return mp.track(action.type, {
reason: getNetworkErrorMessage(action.payload)
});
}
return Promise.resolve();
};

/**
* Transform a {@link CobadgeResponse} into a {@link MixpanelPayload}
* @param response
*/
export const trackCobadgeResponse = (
response: CobadgeResponse
): Record<string, unknown> | undefined =>
response.payload?.searchRequestMetadata?.reduce<Record<string, unknown>>(
(acc, val) => {
if (val.serviceProviderName !== undefined) {
return {
...acc,
[`${val.serviceProviderName}executionStatus`]: val.executionStatus,
[`${val.serviceProviderName}retrievedInstrumentsCount`]: val.retrievedInstrumentsCount
};
}
return acc;
},
{
serviceProviders: response.payload?.searchRequestMetadata?.map(s =>
s.serviceProviderName?.toString()
),
anyServiceError: response.payload?.searchRequestMetadata.some(
m => m.executionStatus === ExecutionStatusEnum.KO
),
anyServicePending: response.payload?.searchRequestMetadata.some(
m => m.executionStatus === ExecutionStatusEnum.PENDING
),
count: response.payload?.paymentInstruments?.length,
status: response.status
} as Record<string, unknown>
);

/**
* Transform a {@link CoBadgeServices} into a {@link MixpanelPayload}
* @param cobadgeSettings
*/
const trackCoBadgeServices = (cobadgeSettings: CoBadgeServices) =>
Object.keys(cobadgeSettings).reduce<Record<string, unknown>>(
(acc, val) => ({ ...acc, [`${val}service`]: cobadgeSettings[val].status }),
{
serviceProviders: Object.keys(cobadgeSettings)
}
);
Empty file.
Empty file.
Empty file.
60 changes: 37 additions & 23 deletions ts/features/wallet/onboarding/cobadge/saga/networking/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,30 @@ import { call, put } from "redux-saga/effects";
import { ActionType } from "typesafe-actions";
import { ContentClient } from "../../../../../../api/content";
import { PaymentManagerClient } from "../../../../../../api/pagopa";
import { mixpanelTrack } from "../../../../../../mixpanel";
import {
isRawCreditCard,
PaymentManagerToken
} from "../../../../../../types/pagopa";
import { SagaCallReturnType } from "../../../../../../types/utils";
import { getNetworkError } from "../../../../../../utils/errors";
import {
getGenericError,
getNetworkError
} from "../../../../../../utils/errors";
import { getPaymentMethodHash } from "../../../../../../utils/paymentMethod";
import { SessionManager } from "../../../../../../utils/SessionManager";
import { convertWalletV2toWalletV1 } from "../../../../../../utils/walletv2";
import { trackCobadgeResponse } from "../../analytics";
import {
addCoBadgeToWallet,
loadCoBadgeAbiConfiguration,
searchUserCoBadge
} from "../../store/actions";
import { onboardingCoBadgeSearchRequestId } from "../../store/reducers/searchCoBadgeRequestId";

const withTokenPrefix = "WALLET_ONBOARDING_COBADGE_SEARCH_WITH_TOKEN";
const withoutTokenPrefix = "WALLET_ONBOARDING_COBADGE_SEARCH_WITHOUT_TOKEN";

/**
* Load the user's cobadge cards. if a previous stored SearchRequestId is found then it will be used
* within the search searchCobadgePans API, otherwise getCobadgePans will be used
Expand All @@ -33,14 +41,21 @@ export function* handleSearchUserCoBadge(
sessionManager: SessionManager<PaymentManagerToken>,
searchAction: ActionType<typeof searchUserCoBadge.request>
) {
const onboardingCoBadgeSearchRequest: ReturnType<typeof onboardingCoBadgeSearchRequestId> = yield select(
onboardingCoBadgeSearchRequestId
);
const trackPrefix = onboardingCoBadgeSearchRequest
? withTokenPrefix
: withoutTokenPrefix;
try {
const onboardingCoBadgeSearchRequest: ReturnType<typeof onboardingCoBadgeSearchRequestId> = yield select(
onboardingCoBadgeSearchRequestId
);
const getPansWithRefresh = sessionManager.withRefresh(
getCobadgePans(searchAction.payload)
);

void mixpanelTrack(`${trackPrefix}_REQUEST`, {
abi: searchAction.payload ?? "all"
});

const getPansWithRefreshResult:
| SagaCallReturnType<typeof getPansWithRefresh>
| SagaCallReturnType<typeof searchCobadgePans> = yield call(
Expand All @@ -53,38 +68,37 @@ export function* handleSearchUserCoBadge(
if (getPansWithRefreshResult.isRight()) {
if (getPansWithRefreshResult.value.status === 200) {
if (getPansWithRefreshResult.value.value.data) {
void mixpanelTrack(
`${trackPrefix}_SUCCESS`,
trackCobadgeResponse(getPansWithRefreshResult.value.value.data)
);
return yield put(
searchUserCoBadge.success(getPansWithRefreshResult.value.value.data)
);
} else {
// it should not never happen
return yield put(
searchUserCoBadge.failure({
kind: "generic",
value: new Error(`data is undefined`)
})
);
const error = getGenericError(new Error(`data is undefined`));
void mixpanelTrack(`${trackPrefix}_FAILURE`, error);
return yield put(searchUserCoBadge.failure(error));
}
} else {
return yield put(
searchUserCoBadge.failure({
kind: "generic",
value: new Error(
`response status ${getPansWithRefreshResult.value.status}`
)
})
const error = getGenericError(
new Error(`response status ${getPansWithRefreshResult.value.status}`)
);
void mixpanelTrack(`${trackPrefix}_FAILURE`, error);
return yield put(searchUserCoBadge.failure(error));
}
} else {
return yield put(
searchUserCoBadge.failure({
kind: "generic",
value: new Error(readableReport(getPansWithRefreshResult.value))
})
const error = getGenericError(
new Error(readableReport(getPansWithRefreshResult.value))
);
void mixpanelTrack(`${trackPrefix}_FAILURE`, error);
return yield put(searchUserCoBadge.failure(error));
}
} catch (e) {
return yield put(searchUserCoBadge.failure(getNetworkError(e)));
const error = getNetworkError(e);
void mixpanelTrack(`${trackPrefix}_FAILURE`, error);
return yield put(searchUserCoBadge.failure(error));
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { fromNullable } from "fp-ts/lib/Option";
import * as pot from "italia-ts-commons/lib/pot";
import _ from "lodash";
import * as React from "react";
import { useEffect } from "react";
import { connect } from "react-redux";
import { Dispatch } from "redux";
import { StatusEnum } from "../../../../../../../definitions/pagopa/cobadge/configuration/CoBadgeService";
import { mixpanelTrack } from "../../../../../../mixpanel";
import { GlobalState } from "../../../../../../store/reducers/types";
import { loadCoBadgeAbiConfiguration } from "../../store/actions";
import { getCoBadgeAbiConfigurationSelector } from "../../store/reducers/abiConfiguration";
Expand All @@ -17,14 +19,21 @@ import LoadAbiConfiguration from "./LoadAbiConfiguration";
type Props = ReturnType<typeof mapDispatchToProps> &
ReturnType<typeof mapStateToProps>;

const eventTrackName = "WALLET_ONBOARDING_COBADGE_SCREEN_START_RESULT";
type StartScreenState = "All" | "Enabled" | "Disabled" | "Unavailable";

const trackOutput = (screenState: StartScreenState, abi?: string) =>
mixpanelTrack(eventTrackName, { screen: screenState, abi });

/**
* The entry screen that orchestrate the different screens
* @constructor
* @param props
*/
const CoBadgeStartScreen = (props: Props): React.ReactElement => {
const CoBadgeStartComponent = (props: Props): React.ReactElement => {
// All ABI selected
if (props.maybeAbiSelected === undefined) {
void trackOutput("All");
return <CoBadgeChosenBankScreen />;
}

Expand All @@ -39,17 +48,35 @@ const CoBadgeStartScreen = (props: Props): React.ReactElement => {
}
switch (props.abiSelectedConfiguration.value) {
case StatusEnum.enabled:
void trackOutput("Enabled", props.maybeAbiSelected);
// Single ABI (bank) screen that allow to start the search
return <CoBadgeChosenBankScreen abi={props.maybeAbiSelected} />;
case StatusEnum.disabled:
void trackOutput("Disabled", props.maybeAbiSelected);
// The chosen ABI is disabled (not yet available)
return <CoBadgeStartKoDisabled />;
case StatusEnum.unavailable:
void trackOutput("Unavailable", props.maybeAbiSelected);
// THe chosen ABI is unavailable (technical problems)
return <CoBadgeStartKoUnavailable />;
}
};

const CoBadgeStartMemo = React.memo(
CoBadgeStartComponent,
(prev: Props, curr: Props) =>
prev.maybeAbiSelected === curr.maybeAbiSelected &&
prev.abiSelectedConfiguration.kind === curr.abiSelectedConfiguration.kind &&
pot.isSome(prev.abiSelectedConfiguration) &&
pot.isSome(curr.abiSelectedConfiguration) &&
_.isEqual(
curr.abiSelectedConfiguration.value,
prev.abiSelectedConfiguration.value
)
);

const CoBadgeStartScreen = (props: Props) => <CoBadgeStartMemo {...props} />;

const mapDispatchToProps = (dispatch: Dispatch) => ({
loadAbiConfig: () => dispatch(loadCoBadgeAbiConfiguration.request())
});
Expand Down
2 changes: 2 additions & 0 deletions ts/store/middlewares/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
isEligibilityResponseTrackable
} from "../../features/bonus/bonusVacanze/utils/bonus";
import { trackBPayAction } from "../../features/wallet/onboarding/bancomatPay/analytics";
import { trackCoBadgeAction } from "../../features/wallet/onboarding/cobadge/analytics";
import { mixpanel } from "../../mixpanel";
import { getCurrentRouteName } from "../../utils/navigation";
import {
Expand Down Expand Up @@ -451,6 +452,7 @@ export const actionTracking = (_: MiddlewareAPI) => (next: Dispatch) => (
void trackBancomatAction(mixpanel)(action);
void trackSatispayAction(mixpanel)(action);
void trackBPayAction(mixpanel)(action);
void trackCoBadgeAction(mixpanel)(action);
}
return next(action);
};
Expand Down

0 comments on commit acbfabe

Please sign in to comment.