Skip to content

Commit

Permalink
mixpanel event
Browse files Browse the repository at this point in the history
  • Loading branch information
fbianchicodermine committed Feb 6, 2025
1 parent e76d5e9 commit 1c5f9f4
Show file tree
Hide file tree
Showing 14 changed files with 146 additions and 58 deletions.
10 changes: 8 additions & 2 deletions packages/pn-personafisica-webapp/src/__mocks__/Auth.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { store } from '../redux/store';
export const mockLogin = async (): Promise<any> => {
const mock = new MockAdapter(authClient);
mock.onPost(AUTH_TOKEN_EXCHANGE()).reply(200, userResponse);
const action = store.dispatch(exchangeToken('mocked-token'));
const action = store.dispatch(exchangeToken({ spidToken: 'mocked-token' }));
mock.reset();
mock.restore();
return action;
Expand All @@ -23,7 +23,7 @@ export const mockAuthentication = () => {
beforeAll(() => {
mock = new MockAdapter(authClient);
mock.onPost(AUTH_TOKEN_EXCHANGE()).reply(200, userResponse);
store.dispatch(exchangeToken('mocked-token'));
store.dispatch(exchangeToken({ spidToken: 'mocked-token' }));
});

afterAll(() => {
Expand All @@ -49,3 +49,9 @@ export const userResponse: User = {
iss: 'https://spid-hub-test.dev.pn.pagopa.it',
jti: 'mockedJTI004',
};

export const userResponseWithRetrievalId: User = {
...userResponse,
retrievalId: 'mocked-retrieval-id',
tppId: 'mocked-tpp-id',
};
74 changes: 53 additions & 21 deletions packages/pn-personafisica-webapp/src/api/auth/Auth.api.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,56 @@
import { User } from "../../redux/auth/types";
import { authClient } from "../apiClients";
import { AUTH_TOKEN_EXCHANGE } from "./auth.routes";
/* eslint-disable functional/immutable-data */
import { AppRouteParams } from '@pagopa-pn/pn-commons';

import { User } from '../../redux/auth/types';
import { authClient } from '../apiClients';
import { AUTH_TOKEN_EXCHANGE } from './auth.routes';

interface TokenExchangeBody {
authorizationToken: string;
retrievalId?: string;
aarQRCodeValue?: string;
}

export interface TokenExchangeRequest {
spidToken: string;
rapidAccess?: [AppRouteParams, string];
}

export const AuthApi = {
exchangeToken: (spidToken: string): Promise<User> =>
authClient.post<User>(AUTH_TOKEN_EXCHANGE(), {authorizationToken: spidToken})
.then((response) => ({
sessionToken: response.data.sessionToken,
email: response.data.email,
name: response.data.name,
family_name: response.data.family_name,
uid: response.data.uid,
fiscal_number: response.data.fiscal_number,
mobile_phone: response.data.mobile_phone,
from_aa: response.data.from_aa,
aud: response.data.aud,
level: response.data.level,
iat: response.data.iat,
exp: response.data.exp,
iss: response.data.iss,
jti: response.data.jti
}))
exchangeToken: async ({ spidToken, rapidAccess }: TokenExchangeRequest): Promise<User> => {
const body: TokenExchangeBody = { authorizationToken: spidToken };
if (rapidAccess) {
const [param, value] = rapidAccess;
if (param === AppRouteParams.AAR) {
body.aarQRCodeValue = value;
}
if (param === AppRouteParams.RETRIEVAL_ID) {
body.retrievalId = value;
}
}
const response = await authClient.post<User>(AUTH_TOKEN_EXCHANGE(), body);
const user: User = {
sessionToken: response.data.sessionToken,
email: response.data.email,
name: response.data.name,
family_name: response.data.family_name,
uid: response.data.uid,
fiscal_number: response.data.fiscal_number,
mobile_phone: response.data.mobile_phone,
from_aa: response.data.from_aa,
aud: response.data.aud,
level: response.data.level,
iat: response.data.iat,
exp: response.data.exp,
iss: response.data.iss,
jti: response.data.jti,
};
if (response.data.retrievalId) {
user.retrievalId = response.data.retrievalId;
}
if (response.data.tppId) {
user.tppId = response.data.tppId;
}
return user;
},
};
Original file line number Diff line number Diff line change
@@ -1,18 +1,40 @@
import MockAdapter from 'axios-mock-adapter';

import { userResponse } from '../../../__mocks__/Auth.mock';
import { AppRouteParams } from '@pagopa-pn/pn-commons';

import { userResponse, userResponseWithRetrievalId } from '../../../__mocks__/Auth.mock';
import { authClient } from '../../apiClients';
import { AuthApi } from '../Auth.api';
import { AUTH_TOKEN_EXCHANGE } from '../auth.routes';

describe('Auth api tests', () => {
it('exchangeToken', async () => {
const token = 'mocked-token';
const mock = new MockAdapter(authClient);
mock.onPost(AUTH_TOKEN_EXCHANGE(), { authorizationToken: token }).reply(200, userResponse);
const res = await AuthApi.exchangeToken(token);
expect(res).toStrictEqual(userResponse);

let mock: MockAdapter;

beforeAll(() => {
mock = new MockAdapter(authClient);
});

afterEach(() => {
mock.reset();
});

afterAll(() => {
mock.restore();
});

it('exchangeToken', async () => {
const spidToken = 'mocked-token';
mock.onPost(AUTH_TOKEN_EXCHANGE(), { authorizationToken: spidToken }).reply(200, userResponse);
const res = await AuthApi.exchangeToken({ spidToken });
expect(res).toStrictEqual(userResponse);
});

it('exchangeToken with rapidAccess', async () => {
const spidToken = 'mocked-token';
const rapidAccess: [AppRouteParams, string] = [AppRouteParams.AAR, 'mocked-qr-code'];
mock.onPost(AUTH_TOKEN_EXCHANGE(), { authorizationToken: spidToken }).reply(200, userResponseWithRetrievalId);
const res = await AuthApi.exchangeToken({ spidToken, rapidAccess });
expect(res).toStrictEqual(userResponseWithRetrievalId);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const RapidAccessGuard = () => {
path = GET_DETTAGLIO_NOTIFICA_PATH(retrievalPayload.originId!);
}

PFEventStrategyFactory.triggerEvent(PFEventsType.SEND_RAPID_ACCESS);
PFEventStrategyFactory.triggerEvent(PFEventsType.SEND_RAPID_ACCESS, { source: param });

const state: NotificationDetailRouteState = { source: param };
navigate(path, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ const SessionGuard = () => {
);
const dispatch = useAppDispatch();
const navigate = useNavigate();
const rapidAccess = useRapidAccessParam();
const sessionCheck = useSessionCheck(200, () => dispatch(logout()));
const { hasApiErrors, hasSpecificStatusError } = useErrors();
const { WORK_IN_PROGRESS } = getConfiguration();
Expand Down Expand Up @@ -157,7 +158,7 @@ const SessionGuard = () => {
const spidToken = getTokenParam();
if (spidToken) {
AppResponsePublisher.error.subscribe('exchangeToken', manageUnforbiddenError);
await dispatch(exchangeToken(spidToken));
await dispatch(exchangeToken({ spidToken, rapidAccess }));
}
};
void performStep(INITIALIZATION_STEPS.USER_DETERMINATION, doUserDetermination);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import {
useIsCancelled,
useIsMobile,
} from '@pagopa-pn/pn-commons';
import { EventNotificationSource } from '@pagopa-pn/pn-commons/src/models/MixpanelEvents';

import DomicileBanner from '../components/DomicileBanner/DomicileBanner';
import LoadingPageWrapper from '../components/LoadingPageWrapper/LoadingPageWrapper';
Expand Down Expand Up @@ -64,18 +63,6 @@ export type NotificationDetailRouteState = {
source?: AppRouteParams; // indicates whether the user arrived to the notification detail page from the QR code
};

const getEventNotificationSource = (
source: AppRouteParams | undefined
): EventNotificationSource => {
if (source === AppRouteParams.AAR) {
return 'QRcode';
}
if (source === AppRouteParams.RETRIEVAL_ID) {
return '3Papp';
}
return 'LISTA_NOTIFICHE';
};

const NotificationDetail: React.FC = () => {
const { id, mandateId } = useParams();
const location = useLocation();
Expand Down Expand Up @@ -455,7 +442,7 @@ const NotificationDetail: React.FC = () => {
notificationStatus: notification.notificationStatus,
checkIfUserHasPayments,
userPayments,
source: getEventNotificationSource(rapidAccessSource),
source: rapidAccessSource,
timeline: notification.timeline,
} as NotificationData);

Expand Down
8 changes: 4 additions & 4 deletions packages/pn-personafisica-webapp/src/redux/auth/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ConsentType, TosPrivacyConsent, parseError } from '@pagopa-pn/pn-common
import { createAsyncThunk } from '@reduxjs/toolkit';

import { apiClient } from '../../api/apiClients';
import { AuthApi } from '../../api/auth/Auth.api';
import { AuthApi, TokenExchangeRequest } from '../../api/auth/Auth.api';
import {
BffTosPrivacyActionBody,
UserConsentsApiFactory,
Expand All @@ -18,11 +18,11 @@ export enum AUTH_ACTIONS {
* Exchange token action between selfcare and pn.
* If token is valid, user info are set in sessionStorage
*/
export const exchangeToken = createAsyncThunk<User, string>(
export const exchangeToken = createAsyncThunk<User, TokenExchangeRequest>(
'exchangeToken',
async (spidToken, { rejectWithValue }) => {
async (request: TokenExchangeRequest, { rejectWithValue }) => {
try {
return await AuthApi.exchangeToken(spidToken);
return await AuthApi.exchangeToken(request);
} catch (e: any) {
return rejectWithValue(parseError(e));
}
Expand Down
2 changes: 2 additions & 0 deletions packages/pn-personafisica-webapp/src/redux/auth/reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const userDataMatcher = yup
iss: yup.string().url(),
jti: yup.string().matches(dataRegex.lettersNumbersAndDashs),
mobile_phone: yup.string().matches(dataRegex.phoneNumber),
retrievalId: yup.string().optional(),
tppId: yup.string().optional(),
})
.noUnknown(true);

Expand Down
2 changes: 2 additions & 0 deletions packages/pn-personafisica-webapp/src/redux/auth/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ export interface User extends BasicUser {
exp: number;
iss: string;
jti: string;
retrievalId?: string; // TODO verificare il nome
tppId?: string; // TODO verificare il nome
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
AppRouteParams,
Downtime,
EventAction,
EventCategory,
Expand All @@ -13,15 +14,15 @@ import {
TimelineCategory,
TrackedEvent,
} from '@pagopa-pn/pn-commons';
import { EventNotificationSource } from '@pagopa-pn/pn-commons/src/models/MixpanelEvents';
import { appRouteParamToEventSource } from '../../notification.utility';

export type NotificationData = {
downtimeEvents: Array<Downtime>;
mandateId: string | undefined;
notificationStatus: NotificationStatus;
checkIfUserHasPayments: boolean;
userPayments: { pagoPaF24: Array<PaymentDetails>; f24Only: Array<F24PaymentDetails> };
source: EventNotificationSource;
source: AppRouteParams | undefined;
timeline: Array<INotificationDetailTimeline>;
};

Expand Down Expand Up @@ -65,8 +66,8 @@ export class SendNotificationDetailStrategy implements EventStrategy {
contains_f24: hasF24 ? 'yes' : 'no',
first_time_opening:
timeline.findIndex((el) => el.category === TimelineCategory.NOTIFICATION_VIEWED) === -1,
source,
source: appRouteParamToEventSource(source) || 'LISTA_NOTIFICHE',
},
};
}
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
import {
AppRouteParams,
EventCategory,
EventPropertyType,
EventStrategy,
TrackedEvent,
} from '@pagopa-pn/pn-commons';
import { EventNotificationSource } from '@pagopa-pn/pn-commons/src/models/MixpanelEvents';

import { appRouteParamToEventSource } from '../../notification.utility';

type Tech = {
source?: EventNotificationSource;
};

export class TechStrategy implements EventStrategy {
performComputations(): TrackedEvent {
performComputations(data?: { source: AppRouteParams }): TrackedEvent<Tech> {
return {
[EventPropertyType.TRACK]: {
event_category: EventCategory.TECH,
source: appRouteParamToEventSource(data?.source),
},
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
AppRouteParams,
DowntimeStatus,
EventAction,
EventCategory,
Expand Down Expand Up @@ -32,7 +33,7 @@ describe('Mixpanel - Notification detail Strategy', () => {
pagoPaF24: paymentsData.pagoPaF24,
f24Only: paymentsData.f24Only,
},
source: 'QRcode',
source: AppRouteParams.AAR,
timeline: timeline,
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EventCategory, EventPropertyType } from '@pagopa-pn/pn-commons';
import { AppRouteParams, EventCategory, EventPropertyType } from '@pagopa-pn/pn-commons';

import { TechStrategy } from '../TechStrategy';

Expand All @@ -13,4 +13,16 @@ describe('Mixpanel - Tech Strategy', () => {
},
});
});

it('should return tech event with additional properties', () => {
const strategy = new TechStrategy();

const techEvent = strategy.performComputations({ source: AppRouteParams.AAR });
expect(techEvent).toEqual({
[EventPropertyType.TRACK]: {
event_category: EventCategory.TECH,
source: 'QRcode',
},
});
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { NotificationDetail } from '@pagopa-pn/pn-commons';
import { AppRouteParams, NotificationDetail } from '@pagopa-pn/pn-commons';
import { EventNotificationSource } from '@pagopa-pn/pn-commons/src/models/MixpanelEvents';

import { NotificationDetailForRecipient } from '../models/NotificationDetail';
import { Delegator } from '../redux/delegation/types';
Expand Down Expand Up @@ -40,3 +41,15 @@ export function parseNotificationDetailForRecipient(
currentRecipientIndex,
};
}

export const appRouteParamToEventSource = (
param: AppRouteParams | undefined
): EventNotificationSource | undefined => {
if (param === AppRouteParams.AAR) {
return 'QRcode';
}
if (param === AppRouteParams.RETRIEVAL_ID) {
return '3Papp';
}
return undefined;
};

0 comments on commit 1c5f9f4

Please sign in to comment.