Skip to content

chore(IT-Wallet): [SIW-3468] Add origin to serialize failure reason to enhance mixpanel tracking on unexpected failures#7684

Merged
DavideOnano merged 26 commits intomasterfrom
SIW-3468/fix-unexpected-serialization
Apr 21, 2026
Merged

chore(IT-Wallet): [SIW-3468] Add origin to serialize failure reason to enhance mixpanel tracking on unexpected failures#7684
DavideOnano merged 26 commits intomasterfrom
SIW-3468/fix-unexpected-serialization

Conversation

@DavideOnano
Copy link
Copy Markdown
Collaborator

@DavideOnano DavideOnano commented Dec 4, 2025

Short description

Add origin context to all “unexpected” IT Wallet analytics events so Mixpanel receives more actionable reasons instead of generic UNEXPECTED values.

List of changes proposed in this pull request

  • Enrich CIE card reading unexpected tracking with event-derived origins and keep structured failure details.
  • Add origin metadata to credential and eID issuance unexpected tracking, wrapping serialized reasons when needed.
  • Extend remote and proximity presentation unexpected tracking to include origin hints and preserve reason structure for Mixpanel.

How to test

Unexpected test cases are unpredictable; in the current state, I couldn't cover the tests for this scenario.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Dec 5, 2025

PR Title Validation for conventional commit type

All good! PR title follows the conventional commit type.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Dec 5, 2025

Jira Pull Request Link

This Pull Request refers to Jira issues:

@codecov
Copy link
Copy Markdown

codecov Bot commented Dec 5, 2025

Codecov Report

❌ Patch coverage is 50.00000% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 61.45%. Comparing base (00e3da8) to head (5e1b5c2).
⚠️ Report is 11 commits behind head on master.

Files with missing lines Patch % Lines
...on/cie/components/ItwCieCardReadFailureContent.tsx 0.00% 3 Missing ⚠️
...twallet/identification/cie/hooks/useCieManager.tsx 0.00% 2 Missing ⚠️
...n/proximity/hooks/useItwProximityEventsTracking.ts 0.00% 1 Missing ⚠️
...ntation/remote/hooks/useItwRemoteEventsTracking.ts 0.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #7684      +/-   ##
==========================================
+ Coverage   58.66%   61.45%   +2.78%     
==========================================
  Files        1990     1882     -108     
  Lines       37796    35817    -1979     
  Branches     6143     5887     -256     
==========================================
- Hits        22173    22011     -162     
+ Misses      14316    12511    -1805     
+ Partials     1307     1295      -12     
Files with missing lines Coverage Δ
ts/features/itwallet/common/utils/itwStoreUtils.ts 77.14% <100.00%> (+2.94%) ⬆️
ts/features/itwallet/issuance/analytics/index.ts 47.27% <ø> (ø)
...llet/issuance/hooks/useCredentialEventsTracking.ts 5.26% <ø> (ø)
...es/itwallet/issuance/hooks/useEidEventsTracking.ts 6.89% <ø> (ø)
...itwallet/presentation/proximity/analytics/index.ts 59.09% <ø> (ø)
...es/itwallet/presentation/remote/analytics/index.ts 81.25% <ø> (ø)
...n/proximity/hooks/useItwProximityEventsTracking.ts 63.63% <0.00%> (ø)
...ntation/remote/hooks/useItwRemoteEventsTracking.ts 84.21% <0.00%> (ø)
...twallet/identification/cie/hooks/useCieManager.tsx 1.92% <0.00%> (ø)
...on/cie/components/ItwCieCardReadFailureContent.tsx 3.63% <0.00%> (-0.07%) ⬇️

... and 166 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 070a1ae...5e1b5c2. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@gispada
Copy link
Copy Markdown
Collaborator

gispada commented Dec 17, 2025

I am not totally convinced of this new origin property that takes a few hardcoded values. Does it really help when dealing with serialization issues? I thought we had to improve [object Object] messages.

Was it asked explicitly by the analytics team?

@DavideOnano
Copy link
Copy Markdown
Collaborator Author

@gispada
We decided to add this new key together with @MonicaRungi-EY to at least get an idea of where the error originates.
Unclear values such as [object Object], empty string and similar are received by the client as response payload, and we haven’t found an effective way to handle them on our side so far.

Comment thread ts/features/itwallet/issuance/hooks/useCredentialEventsTracking.ts Outdated
Comment on lines 75 to 79
const reason = !failure.reason
? "Reason not provided"
: failure.reason instanceof Error
? createReasonObject(failure.reason.message)
? createReasonObject(failure.reason.message, origin)
: failure.reason;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When failure.reason is not an Error (e.g., it's already a structured object), the origin parameter is ignored entirely.

What do you think about doing something like this?

const reason = !failure.reason
    ? createReasonObject("Reason not provided", origin)
    : failure.reason instanceof Error
    ? createReasonObject(failure.reason.message, origin)
    : typeof failure.reason === "object"
    ? { ...failure.reason, origin }
    : failure.reason;

Copy link
Copy Markdown
Collaborator Author

@DavideOnano DavideOnano Mar 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sense to me.
Should we make a guard to avoid concatenating so many ternary and extends the check to more types of entities?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I forgot to answer your question. If you think it is useful sure, it should be fine.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added it.

Lmk if anything else should be adjusted


export type TrackItWalletCieCardReadingUnexpectedFailure = {
reason: string | undefined;
reason: string | unknown | undefined;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reason is string | unknown | undefined;, but in the code is always an object with origin and name. Is there a reason for this?
Btw, if we want to enrich the event we should prefer adding a new property instead of changing the ones already present.

Copy link
Copy Markdown
Collaborator Author

@DavideOnano DavideOnano Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added unknown because reason was no longer a plain string in this event: we started sending { origin, name }. Keeping origin inside reason was agreed with the CXM team for these unexpected failures.
Re-thinking about it adding unknown doesn't give any advantage. I replaced it with the curent shape

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not change properties that we already sending to Mixpanel. Instead, we should extend them. In this case we should have:

export type TrackItWalletCieCardReadingUnexpectedFailure = {
  reason: string | undefined;
  origin: string | undefined;
  cie_reading_progress: number;
  itw_flow: ItwFlow;
  ITW_ID_method?: ItwIdMethod;
};

This will not break the previous event tracking

Copy link
Copy Markdown
Collaborator Author

@DavideOnano DavideOnano Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, I was looking at the serializer.
Atp should we allso modify the current serialization in itwStoreUtils.tsx? Instead of creating an object with extended reason, maybe we can extend the shape with origin outside.
What do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean? Can you give me an example?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about 2 possible solution:
Instead of creating an object with origin inside:

const createReasonObject = (message: string, origin?: string) => ({
  code: "UNEXPECTED",
  errorDescription: message,
  origin
});

We can pass it outside in the function, something like:

export const serializeFailureReason = (
  failure:
    | IssuanceFailure
    | CredentialIssuanceFailure
    | RemoteFailure
    | ProximityFailure,
  origin?: string
) => ({
  ...failure,
  reason: mapFailureReason(failure.reason),
  origin
});

This could actually may be misleading because this function is probably born to recreate reason.
A better solution could be instead of passing the origin at the function as is:

return trackItwIdRequestUnexpectedFailure(
        shouldSerializeReason(failure)
          ? {
              ...serializeFailureReason(failure, "ITW_EID_EVENTS_TRACKING"),
              itw_flow: itwFlow
            }
          : { ...failure, itw_flow: itwFlow }
      );

We can maybe populate it outside, something like:

return trackItwIdRequestUnexpectedFailure(
        shouldSerializeReason(failure)
          ? {
              ...serializeFailureReason(failure),
              itw_flow: itwFlow,
              origin: 'ITW_EID_EVENTS_TRACKING'
            }
          : { ...failure, itw_flow: itwFlow }
      );

I'm not sure about this because the request was to give more insight on reason, and doing so it feels like we are losing the focus.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes serializeFailureReason doesn't need to have origin. Let's keep things simple

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I extracted the logic to keep origin outside serializeFailureReason.
The parameter origin is now sent directly from the event

Comment on lines +91 to +92
const isReasonObject = (reason: unknown): reason is object =>
typeof reason === "object" && reason !== null;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think that having a dedicated function for typeof reason === "object" is overkill?

Copy link
Copy Markdown
Collaborator Author

@DavideOnano DavideOnano Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this point yes, is only used once. I think inlining it would be better

Copy link
Copy Markdown
Contributor

@mastro993 mastro993 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants