Skip to content

Commit

Permalink
chore: [IOCOM-1561] FIMS history saga (#5920)
Browse files Browse the repository at this point in the history
## Short description
This pr adds the implementation and barebones testing UI for the FIMS
history flow

## List of changes proposed in this pull request
- required Saga and actions
- typedef to use with the client generator for FIMS' history calls
- updated io-services-metadata definitions in order to include the newly
added feature flag
- listItem in privacyHomeScreen to show the FIMS history, hidden by the
new feature flag
- required selectors
- new FIMS client
- rename of FIMS features to not use uppercase characters
- various navigators entries where required

## How to test
running `io-dev-server`, while checked out in the `feature/fims` branch,
go into profile>privacy and click the `FIMS_HISTORY` listItem, you
should be greeted by a list of dates, which are the FIMS access dates.

complete UI and export request to come in future PRs

---------

Co-authored-by: Andrea <andrea.piai@pagopa.it>
  • Loading branch information
forrest57 and Vangaorth authored Jul 4, 2024
1 parent e02827d commit 8542d6d
Show file tree
Hide file tree
Showing 20 changed files with 419 additions and 66 deletions.
128 changes: 128 additions & 0 deletions assets/FimsSwager.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
openapi: 3.0.0
info:
title: IO (FIMS)
version: 1.0.0

security:
- Bearer: []

paths:
/api/v1/fims/consents:
get:
operationId: getConsents
parameters:
- $ref: "#/components/parameters/continuationToken"
responses:
200:
description: "OK"
content:
application/json:
schema:
$ref: "#/components/schemas/ConsentsResponseDTO"
401:
description: Bearer token null or expired.
502:
description: Bad gateway.
504:
description: Gateway timeout.
500:
description: Service unavailable.
content:
application/json:
schema:
$ref: "#/components/schemas/ProblemJson"

/api/v1/fims/exports:
post:
operationId: exports
requestBody:
$ref: "#/components/requestBodies/Export"
responses:
202:
description: Accepted
409:
description: Conflict
401:
description: Bearer token null or expired.
502:
description: Bad gateway.
504:
description: Gateway timeout.
500:
description: Service unavailable.
content:
application/json:
schema:
$ref: "#/components/schemas/ProblemJson"


components:
securitySchemes:
Bearer:
type: http
scheme: bearer
bearerFormat: JWT

parameters:
continuationToken:
name: continuationToken
in: query
required: false
schema:
type: string

requestBodies:
Export:
description: Require an export
required: true
content:
application/json:
schema:
type: object
properties:
type:
type: string
enum: ["consent"]
required:
- type

schemas:
ConsentsResponseDTO:
type: object
properties:
items:
type: array
items:
$ref: "#/components/schemas/Consent"
continuationToken:
type: string
required:
- items
- continuationToken

ProblemJson:
$ref: "https://raw.githubusercontent.com/pagopa/io-functions-commons/v26.3.0/openapi/definitions.yaml#/ProblemJson"

Consent:
type: object
properties:
id:
type: string
format: ulid
service_id:
type: string
format: ulid
redirect:
type: object
properties:
uri:
type: string
format: uri
display_name:
type: string
timestamp:
$ref: "https://raw.githubusercontent.com/pagopa/io-functions-commons/v26.3.0/openapi/definitions.yaml#/Timestamp"
required:
- id
- service_id
- timestamp
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "2.63.0-rc.5",
"io_backend_api": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/api_backend.yaml",
"io_public_api": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/api_public.yaml",
"io_content_specs": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.33/definitions.yml",
"io_content_specs": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.34/definitions.yml",
"io_cgn_specs": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/api_cgn.yaml",
"io_cgn_merchants_specs": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/api_cgn_operator_search.yaml",
"api_fci": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/api_io_sign.yaml",
Expand All @@ -13,9 +13,10 @@
"io_consumed_pn_specs": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/openapi/consumed/api-piattaforma-notifiche.yaml",
"api_cdc": "assets/CdcSwagger.yml",
"pagopa_api": "assets/paymentManager/spec.json",
"pagopa_api_walletv2": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.32/bonus/specs/bpd/pm/walletv2.json",
"pagopa_cobadge_configuration": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.32/pagopa/cobadge/abi_definitions.yml",
"pagopa_privative_configuration": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.32/pagopa/privative/definitions.yml",
"fims_swagger": "assets/FimsSwager.yml",
"pagopa_api_walletv2": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.34/bonus/specs/bpd/pm/walletv2.json",
"pagopa_cobadge_configuration": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.34/pagopa/cobadge/abi_definitions.yml",
"pagopa_privative_configuration": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.34/pagopa/privative/definitions.yml",
"idpay_api": "https://raw.githubusercontent.com/pagopa/cstar-infrastructure/v6.9.1/src/domains/idpay-app/api/idpay_appio_full/openapi.appio.full.yml",
"services_api": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/api_services_app_backend.yaml",
"lollipop_api": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/api_lollipop_first_consumer.yaml",
Expand Down Expand Up @@ -86,6 +87,7 @@
"generate:pagopa-ecommerce-api": "rimraf definitions/pagopa/ecommerce && mkdir -p definitions/pagopa/ecommerce && gen-api-models --api-spec $npm_package_pagopa_api_ecommerce --out-dir ./definitions/pagopa/ecommerce --no-strict --response-decoders --request-types --client",
"generate:pagopa-biz-events-api": "rimraf definitions/pagopa/biz-events && mkdir -p definitions/pagopa/biz-events && gen-api-models --api-spec $npm_package_pagopa_api_biz_events --out-dir ./definitions/pagopa/biz-events --no-strict --response-decoders --request-types --client",
"generate:pagopa-platform-api": "rimraf definitions/pagopa/platform && mkdir -p definitions/pagopa/platform && gen-api-models --api-spec $npm_package_pagopa_api_platform --out-dir ./definitions/pagopa/platform --no-strict --response-decoders --request-types --client",
"generate:fims": "rimraf definitions/fims && mkdir -p definitions/fims && gen-api-models --api-spec $npm_package_fims_swagger --out-dir ./definitions/fims --no-strict --response-decoders --request-types --client",
"generate:payments": "npm-run-all generate:pagopa-walletv3-api generate:pagopa-ecommerce-api generate:pagopa-biz-events-api generate:pagopa-platform-api",
"generate": "npm-run-all generate:*",
"locales_unused": "ts-node --skip-project -O '{\"lib\":[\"es2015\"]}' scripts/unused-locales.ts",
Expand Down
37 changes: 37 additions & 0 deletions ts/features/fims/common/navigation/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { createStackNavigator } from "@react-navigation/stack";
import * as React from "react";
import { FimsHistoryScreen } from "../../history/screens/HistoryScreen";
import {
FimsFlowHandlerScreen,
FimsFlowHandlerScreenRouteParams
} from "../../singleSignOn/screens/FimsFlowHandlerScreen";

export const FIMS_ROUTES = {
MAIN: "FIMS_MAIN",
CONSENTS: "FIMS_SSO_CONSENTS",
HISTORY: "FIMS_HISTORY"
} as const;

export type FimsParamsList = {
[FIMS_ROUTES.MAIN]: undefined;
[FIMS_ROUTES.CONSENTS]: FimsFlowHandlerScreenRouteParams;
[FIMS_ROUTES.HISTORY]: undefined;
};

const Stack = createStackNavigator<FimsParamsList>();

export const FimsNavigator = () => (
<Stack.Navigator
initialRouteName={FIMS_ROUTES.MAIN}
// Make sure to disable gestures in order to prevent
// the user from going back by swiping and thus not
// calling the custom cancel logic
screenOptions={{ gestureEnabled: false, headerShown: true }}
>
<Stack.Screen
name={FIMS_ROUTES.CONSENTS}
component={FimsFlowHandlerScreen}
/>
<Stack.Screen name={FIMS_ROUTES.HISTORY} component={FimsHistoryScreen} />
</Stack.Navigator>
);
16 changes: 15 additions & 1 deletion ts/features/fims/common/saga/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
import { SagaIterator } from "redux-saga";
import { fork } from "typed-redux-saga/macro";
import { fork, select } from "typed-redux-saga/macro";
import { apiUrlPrefix } from "../../../../config";
import { sessionTokenSelector } from "../../../../store/reducers/authentication";
import { createFimsClient } from "../../history/api/client";
import { watchFimsHistorySaga } from "../../history/saga";
import { watchFimsSSOSaga } from "../../singleSignOn/saga";

const FIMS_DEV_ENV_TOKEN = "";

export function* watchFimsSaga(): SagaIterator {
const fimsClient = createFimsClient(apiUrlPrefix);

const sessionToken = yield* select(sessionTokenSelector);
yield* fork(watchFimsSSOSaga);
yield* fork(
watchFimsHistorySaga,
fimsClient,
sessionToken ?? FIMS_DEV_ENV_TOKEN
);
}
3 changes: 2 additions & 1 deletion ts/features/fims/common/store/actions.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { FimsHistoryActions } from "../../history/store/actions";
import { FimsSSOActions } from "../../singleSignOn/store/actions";

export type FimsActions = FimsSSOActions;
export type FimsActions = FimsSSOActions | FimsHistoryActions;
9 changes: 7 additions & 2 deletions ts/features/fims/common/store/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ import { combineReducers } from "redux";
import fimsSSOReducer, {
FimsSSOState
} from "../../singleSignOn/store/reducers/index";
import fimsHistoryReducer, {
FimsHistoryState
} from "../../history/store/reducer";

export type FimsState = {
SSO: FimsSSOState;
sso: FimsSSOState;
history: FimsHistoryState;
};

const fimsReducer = combineReducers({
SSO: fimsSSOReducer
sso: fimsSSOReducer,
history: fimsHistoryReducer
});

export default fimsReducer;
10 changes: 10 additions & 0 deletions ts/features/fims/history/api/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { createClient } from "../../../../../definitions/fims/client";
import { defaultRetryingFetch } from "../../../../utils/fetch";

export const createFimsClient = (baseUrl: string) =>
createClient({
baseUrl,
fetchApi: defaultRetryingFetch()
});

export type FimsHistoryClient = ReturnType<typeof createFimsClient>;
49 changes: 49 additions & 0 deletions ts/features/fims/history/saga/handleGetFimsHistorySaga.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import * as E from "fp-ts/lib/Either";
import { pipe } from "fp-ts/lib/function";
import { call, put } from "typed-redux-saga/macro";
import { ActionType } from "typesafe-actions";
import { SagaCallReturnType } from "../../../../types/utils";
import { withRefreshApiCall } from "../../../fastLogin/saga/utils";
import { FimsHistoryClient } from "../api/client";
import { fimsHistoryGet } from "../store/actions";

export function* handleGetFimsHistorySaga(
getFimsHistory: FimsHistoryClient["getConsents"],
bearerToken: string,
action: ActionType<typeof fimsHistoryGet.request>
) {
const getHistoryRequest = getFimsHistory({
Bearer: bearerToken,
continuationToken: action.payload.continuationToken
});

try {
const getHistoryResult = (yield* call(
withRefreshApiCall,
getHistoryRequest,
action
)) as SagaCallReturnType<typeof getFimsHistory>;

const resultAction = yield* call(
extractFimsHistoryResponseAction,
getHistoryResult
);
yield* put(resultAction);
} catch (e) {
yield* put(fimsHistoryGet.failure((e as Error).toString()));
}
}

const extractFimsHistoryResponseAction = (
historyResult: SagaCallReturnType<FimsHistoryClient["getConsents"]>
) =>
pipe(
historyResult,
E.fold(
error => fimsHistoryGet.failure(error.toString()),
response =>
response.status === 200
? fimsHistoryGet.success(response.value)
: fimsHistoryGet.failure("GENERIC_NON_200")
)
);
16 changes: 16 additions & 0 deletions ts/features/fims/history/saga/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { takeLatest } from "typed-redux-saga/macro";
import { FimsHistoryClient } from "../api/client";
import { fimsHistoryGet } from "../store/actions";
import { handleGetFimsHistorySaga } from "./handleGetFimsHistorySaga";

export function* watchFimsHistorySaga(
client: FimsHistoryClient,
bearerToken: string
) {
yield* takeLatest(
fimsHistoryGet.request,
handleGetFimsHistorySaga,
client.getConsents,
bearerToken
);
}
37 changes: 37 additions & 0 deletions ts/features/fims/history/screens/HistoryScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { VSpacer } from "@pagopa/io-app-design-system";
import * as pot from "@pagopa/ts-commons/lib/pot";
import * as React from "react";
import { SafeAreaView, Text, View } from "react-native";
import LoadingScreenContent from "../../../../components/screens/LoadingScreenContent";
import { useHeaderSecondLevel } from "../../../../hooks/useHeaderSecondLevel";
import { useIODispatch, useIOSelector } from "../../../../store/hooks";
import { fimsHistoryGet } from "../store/actions";
import { fimsHistoryPotSelector } from "../store/selectors";

export const FimsHistoryScreen = () => {
const dispatch = useIODispatch();
const historyPot = useIOSelector(fimsHistoryPotSelector);

React.useEffect(() => {
dispatch(fimsHistoryGet.request({}));
}, [dispatch]);

useHeaderSecondLevel({
title: "History"
});
if (pot.isLoading(historyPot)) {
return <LoadingScreenContent contentTitle="" />;
}
const history = pot.toUndefined(historyPot)?.items ?? [];

return (
<SafeAreaView style={{ flex: 1 }}>
{history.map((item, index) => (
<View key={index}>
<Text>{item.timestamp.toDateString()}</Text>
<VSpacer size={8} />
</View>
))}
</SafeAreaView>
);
};
14 changes: 14 additions & 0 deletions ts/features/fims/history/store/actions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ActionType, createAsyncAction } from "typesafe-actions";
import { ConsentsResponseDTO } from "../../../../../../definitions/fims/ConsentsResponseDTO";

export type FimsHistoryGetPayloadType = {
continuationToken?: string;
};

export const fimsHistoryGet = createAsyncAction(
"FIMS_GET_HISTORY_REQUEST",
"FIMS_GET_HISTORY_SUCCESS",
"FIMS_GET_HISTORY_FAILURE"
)<FimsHistoryGetPayloadType, ConsentsResponseDTO, string>();

export type FimsHistoryActions = ActionType<typeof fimsHistoryGet>;
Loading

0 comments on commit 8542d6d

Please sign in to comment.