Skip to content

Commit

Permalink
feat(Bonus Pagamenti Digitali): [#175796418] Send support token withi…
Browse files Browse the repository at this point in the history
…n bug report (#2409)

* [#175796418] request and store support token

* [#175796418] action/reduces/instabug log

* [#175796418] fix merge

* [#175796418] move token load from saga to CH component mount

* [#175796418] update spec api public

* [#175796418] update API endopoint

* [#175796418] update spec endopoint

* [#175796418] fix merge conflicts
  • Loading branch information
Undermaken authored Nov 26, 2020
1 parent 09ed3f6 commit ff4b0f6
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 18 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "italia-app",
"version": "1.10.0-rc.1",
"io_backend_api": "https://raw.githubusercontent.com/pagopa/io-backend/v7.8.0/api_backend.yaml",
"io_public_api": "https://raw.githubusercontent.com/pagopa/io-backend/v7.8.0/api_public.yaml",
"io_backend_api": "https://raw.githubusercontent.com/pagopa/io-backend/v7.9.0/api_backend.yaml",
"io_public_api": "https://raw.githubusercontent.com/pagopa/io-backend/v7.9.0/api_public.yaml",
"io_content_specs": "https://raw.githubusercontent.com/pagopa/io-services-metadata/master/definitions.yml",
"io_bonus_vacanze_specs": "https://raw.githubusercontent.com/pagopa/io-backend/master/api_bonus.yaml",
"io_bpd_citizen": "https://raw.githubusercontent.com/pagopa/io-services-metadata/master/bonus/specs/bpd/citizen.json",
Expand Down
13 changes: 13 additions & 0 deletions ts/api/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import {
GetServiceT,
getSessionStateDefaultDecoder,
GetSessionStateT,
getSupportTokenDefaultDecoder,
GetSupportTokenT,
getUserDataProcessingDefaultDecoder,
GetUserDataProcessingT,
getUserMessageDefaultDecoder,
Expand Down Expand Up @@ -367,6 +369,14 @@ export function BackendClient(
response_decoder: getActivationStatusDefaultDecoder()
};

const getSupportToken: GetSupportTokenT = {
method: "get",
url: () => `/api/v1/token/support`,
headers: tokenHeaderProducer,
query: () => ({}),
response_decoder: getSupportTokenDefaultDecoder()
};

// withBearerToken injects the field 'Baerer' with value token into the parameter P
// of the f function
const withBearerToken = <P extends { Bearer: string }, R>(
Expand Down Expand Up @@ -432,6 +442,9 @@ export function BackendClient(
postUserDataProcessingRequest: withBearerToken(
createFetchRequestForApi(postUserDataProcessingT, options)
),
getSupportToken: withBearerToken(
createFetchRequestForApi(getSupportToken, options)
),
deleteUserDataProcessingRequest: withBearerToken(
createFetchRequestForApi(deleteUserDataProcessingT, options)
)
Expand Down
22 changes: 19 additions & 3 deletions ts/components/ContextualHelpModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ import { Dispatch } from "../store/actions/types";
import { screenContextualHelpDataSelector } from "../store/reducers/content";
import { GlobalState } from "../store/reducers/types";
import themeVariables from "../theme/variables";
import {
supportTokenSelector,
SupportTokenState
} from "../store/reducers/authentication";
import { loadSupportToken } from "../store/actions/authentication";
import {
FAQsCategoriesType,
FAQType,
Expand All @@ -37,7 +42,10 @@ type OwnProps = Readonly<{
onLinkClicked?: (url: string) => void;
modalAnimation?: ModalBaseProps["animationType"];
close: () => void;
onRequestAssistance: (type: BugReporting.reportType) => void;
onRequestAssistance: (
type: BugReporting.reportType,
supportToken: SupportTokenState
) => void;
faqCategories?: ReadonlyArray<FAQsCategoriesType>;
}>;

Expand Down Expand Up @@ -82,6 +90,8 @@ const ContextualHelpModal: React.FunctionComponent<Props> = (props: Props) => {
) {
props.loadContextualHelpData();
}
// refresh / load support token
props.loadSupportToken();
}, [
pot.isNone(props.potContextualData) || pot.isError(props.potContextualData)
]);
Expand Down Expand Up @@ -181,7 +191,9 @@ const ContextualHelpModal: React.FunctionComponent<Props> = (props: Props) => {
<React.Fragment>
<View spacer={true} extralarge={true} />
<InstabugAssistanceComponent
requestAssistance={props.onRequestAssistance}
requestAssistance={reportType =>
props.onRequestAssistance(reportType, props.supportToken)
}
/>
</React.Fragment>
)}
Expand All @@ -197,14 +209,18 @@ const ContextualHelpModal: React.FunctionComponent<Props> = (props: Props) => {
const mapStateToProps = (state: GlobalState) => {
const potContextualData = screenContextualHelpDataSelector(state);
const maybeContextualData = pot.getOrElse(potContextualData, none);

const supportToken = supportTokenSelector(state);
return {
supportToken,
potContextualData,
maybeContextualData
};
};

const mapDispatchToProps = (dispatch: Dispatch) => ({
loadContextualHelpData: () => dispatch(loadContextualHelpData.request())
loadContextualHelpData: () => dispatch(loadContextualHelpData.request()),
loadSupportToken: () => dispatch(loadSupportToken.request())
});

export default connect(
Expand Down
20 changes: 17 additions & 3 deletions ts/components/screens/BaseScreenComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import * as React from "react";
import { ModalBaseProps, Platform } from "react-native";
import { TranslationKeys } from "../../../locales/locales";
import {
instabugLog,
TypeLogs,
openInstabugQuestionReport,
openInstabugReplies
} from "../../boot/configureInstabug";
Expand All @@ -28,6 +30,8 @@ import {
deriveCustomHandledLink,
isIoInternalLink
} from "../ui/Markdown/handlers/link";
import { SupportTokenState } from "../../store/reducers/authentication";
import { getValueOrElse } from "../../features/bonus/bpd/model/RemoteValue";
import { AccessibilityEvents, BaseHeader } from "./BaseHeader";

export interface ContextualHelpProps {
Expand Down Expand Up @@ -59,6 +63,7 @@ type Props = OwnProps &
interface State {
isHelpVisible: boolean;
requestReport: Option<BugReporting.reportType>;
supportToken?: SupportTokenState;
markdownContentLoaded: Option<boolean>;
contextualHelpModalAnimation: ModalBaseProps["animationType"];
}
Expand All @@ -84,11 +89,14 @@ class BaseScreenComponent extends React.PureComponent<Props, State> {
};
}

private handleOnRequestAssistance = (type: BugReporting.reportType) => {
private handleOnRequestAssistance = (
type: BugReporting.reportType,
supportToken: SupportTokenState
) => {
// don't close modal if the report isn't a bug (bug brings a screenshot)
if (type !== BugReporting.reportType.bug) {
this.setState(
{ requestReport: some(type) },
{ requestReport: some(type), supportToken },
this.handleOnContextualHelpDismissed
);
return;
Expand All @@ -101,7 +109,7 @@ class BaseScreenComponent extends React.PureComponent<Props, State> {
});
this.setState({ contextualHelpModalAnimation }, () => {
this.setState({ isHelpVisible: false }, () => {
this.setState({ requestReport: some(type) }, () => {
this.setState({ requestReport: some(type), supportToken }, () => {
// since in Android we have no way to handle Modal onDismiss event https://reactnative.dev/docs/modal#ondismiss
// we force handling here. The timeout is due to wait until the modal is completely hidden
// otherwise in the Instabug screeshoot we will see the contextual help content instead the screen below
Expand All @@ -120,6 +128,12 @@ class BaseScreenComponent extends React.PureComponent<Props, State> {
const maybeReport = this.state.requestReport;
this.setState({ requestReport: none }, () => {
maybeReport.map(type => {
fromNullable(this.state.supportToken)
.mapNullable(rsp => getValueOrElse(rsp, undefined))
.map(st => {
instabugLog(JSON.stringify(st), TypeLogs.INFO, "support-token");
});

switch (type) {
case BugReporting.reportType.bug:
openInstabugQuestionReport();
Expand Down
8 changes: 6 additions & 2 deletions ts/sagas/startup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,12 @@ export function* initializeApplicationSaga(): Generator<Effect, void, any> {
// Start watching for requests of refresh the profile
yield fork(watchProfileRefreshRequestsSaga, backendClient.getProfile);

// Start watching for requests of checkSession
yield fork(watchCheckSessionSaga, backendClient.getSession);
// Start watching for requests about session and support token
yield fork(
watchCheckSessionSaga,
backendClient.getSession,
backendClient.getSupportToken
);

// Sart watching for request of remove profile
yield takeEvery(removeAccountMotivation, handleRemoveAccount);
Expand Down
13 changes: 8 additions & 5 deletions ts/sagas/startup/__tests__/watchCheckSessionSaga.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import {
sessionExpired,
sessionInformationLoadSuccess
} from "../../../store/actions/authentication";
import { checkSession, checkSessionResult } from "../watchCheckSessionSaga";
import {
testableCheckSession,
checkSessionResult
} from "../watchCheckSessionSaga";

describe("checkSession", () => {
const getSessionValidity = jest.fn();
Expand All @@ -21,7 +24,7 @@ describe("checkSession", () => {
status: 200,
value: responseValue
});
testSaga(checkSession, getSessionValidity)
testSaga(testableCheckSession!, getSessionValidity)
.next()
.call(getSessionValidity, {})
.next(responseOK)
Expand All @@ -38,7 +41,7 @@ describe("checkSession", () => {

it("if response is 401 the session is invalid", () => {
const responseUnauthorized = right({ status: 401 });
testSaga(checkSession, getSessionValidity)
testSaga(testableCheckSession!, getSessionValidity)
.next()
.call(getSessionValidity, {})
.next(responseUnauthorized)
Expand All @@ -53,7 +56,7 @@ describe("checkSession", () => {

it("if response is 500 the session is valid", () => {
const response500 = right({ status: 500 });
testSaga(checkSession, getSessionValidity)
testSaga(testableCheckSession!, getSessionValidity)
.next()
.call(getSessionValidity, {})
.next(response500)
Expand All @@ -72,7 +75,7 @@ describe("checkSession", () => {
context: [{ key: "", type: t.string }]
};
const responeLeft = left([validatorError]);
testSaga(checkSession, getSessionValidity)
testSaga(testableCheckSession!, getSessionValidity)
.next()
.call(getSessionValidity, {})
.next(responeLeft)
Expand Down
38 changes: 36 additions & 2 deletions ts/sagas/startup/watchCheckSessionSaga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,37 @@ import { getType } from "typesafe-actions";
import { BackendClient } from "../../api/backend";
import {
checkCurrentSession,
loadSupportToken,
sessionExpired,
sessionInformationLoadSuccess
} from "../../store/actions/authentication";
import { SagaCallReturnType } from "../../types/utils";
import { isTestEnv } from "../../utils/environment";

export function* checkSession(
// load the support token useful for user assistance
function* handleLoadSupportToken(
getSupportToken: ReturnType<typeof BackendClient>["getSupportToken"]
): SagaIterator {
try {
const response: SagaCallReturnType<typeof getSupportToken> = yield call(
getSupportToken,
{}
);
if (response.isLeft()) {
throw Error(readableReport(response.value));
} else {
if (response.value.status === 200) {
yield put(loadSupportToken.success(response.value.value));
} else {
throw Error(`response status code ${response.value.status}`);
}
}
} catch (error) {
yield put(loadSupportToken.failure(error));
}
}

function* checkSession(
getSessionValidity: ReturnType<typeof BackendClient>["getSession"]
): SagaIterator {
try {
Expand Down Expand Up @@ -49,12 +74,21 @@ export function* checkSessionResult(

// Saga that listen to check session dispatch and returns it's validity
export function* watchCheckSessionSaga(
getSessionValidity: ReturnType<typeof BackendClient>["getSession"]
getSessionValidity: ReturnType<typeof BackendClient>["getSession"],
getSupportToken: ReturnType<typeof BackendClient>["getSupportToken"]
): SagaIterator {
yield takeLatest(
getType(checkCurrentSession.request),
checkSession,
getSessionValidity
);
yield takeLatest(getType(checkCurrentSession.success), checkSessionResult);

yield takeLatest(
getType(loadSupportToken.request),
handleLoadSupportToken,
getSupportToken
);
}

export const testableCheckSession = isTestEnv ? checkSession : undefined;
10 changes: 9 additions & 1 deletion ts/store/actions/authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { PasswordLogin } from "../../../definitions/backend/PasswordLogin";
import { PublicSession } from "../../../definitions/backend/PublicSession";
import { IdentityProvider } from "../../models/IdentityProvider";
import { SessionToken } from "../../types/SessionToken";
import { SupportToken } from "../../../definitions/backend/SupportToken";

export type LogoutOption = {
keepUserData: boolean;
Expand Down Expand Up @@ -81,6 +82,12 @@ export const checkCurrentSession = createAsyncAction(
"CHECK_CURRENT_SESSION_FAILURE"
)<void, CheckSessionResult, Error>();

export const loadSupportToken = createAsyncAction(
"LOAD_TOKEN_SUPPORT_REQUEST",
"LOAD_TOKEN_SUPPORT_SUCCESS",
"LOAD_TOKEN_SUPPORT_FAILURE"
)<void, SupportToken, Error>();

export const sessionExpired = createStandardAction("SESSION_EXPIRED")();

export const sessionInvalid = createStandardAction("SESSION_INVALID")();
Expand All @@ -99,4 +106,5 @@ export type AuthenticationActions =
| ActionType<typeof checkCurrentSession>
| ActionType<typeof sessionExpired>
| ActionType<typeof sessionInvalid>
| ActionType<typeof resetAuthenticationState>;
| ActionType<typeof resetAuthenticationState>
| ActionType<typeof loadSupportToken>;
Loading

0 comments on commit ff4b0f6

Please sign in to comment.