Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(vue): Add Pinia plugin #13841

Merged
merged 10 commits into from
Oct 15, 2024
Merged

feat(vue): Add Pinia plugin #13841

merged 10 commits into from
Oct 15, 2024

Conversation

onurtemizkan
Copy link
Collaborator

@onurtemizkan onurtemizkan commented Oct 1, 2024

Resolves: #13279
Depends on: #13840
Sample Event

Docs PR: getsentry/sentry-docs#11516

Adds a Pinia plugin with a feature set similar to the Redux integration.

  • Attaches Pinia state as an attachment to the event (true by default)
  • Provides actionTransformer and stateTransformer to the user for potentially required PII modifications.
  • Adds breadcrumbs for Pinia actions
  • Assigns Pinia state to event contexts.

Copy link

codecov bot commented Oct 1, 2024

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
626 1 625 31
View the top 1 failed tests by shortest run time
pinia.test.ts sends pinia action breadcrumbs and state context
Stack Traces | 0.316s run time
pinia.test.ts:4:1 sends pinia action breadcrumbs and state context

To view individual test run time comparison to the main branch, go to the Test Analytics Dashboard

Copy link
Contributor

github-actions bot commented Oct 2, 2024

size-limit report 📦

Path Size % Change Change
@sentry/browser 22.73 KB - -
@sentry/browser - with treeshaking flags 21.53 KB - -
@sentry/browser (incl. Tracing) 35.01 KB - -
@sentry/browser (incl. Tracing, Replay) 71.67 KB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 62.07 KB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 76.01 KB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 88.78 KB - -
@sentry/browser (incl. Tracing, Replay, Feedback, metrics) 90.63 KB - -
@sentry/browser (incl. metrics) 27 KB - -
@sentry/browser (incl. Feedback) 39.87 KB - -
@sentry/browser (incl. sendFeedback) 27.38 KB - -
@sentry/browser (incl. FeedbackAsync) 32.17 KB - -
@sentry/react 25.49 KB - -
@sentry/react (incl. Tracing) 37.97 KB - -
@sentry/vue 26.91 KB - -
@sentry/vue (incl. Tracing) 36.9 KB - -
@sentry/svelte 22.87 KB - -
CDN Bundle 24.11 KB - -
CDN Bundle (incl. Tracing) 36.84 KB - -
CDN Bundle (incl. Tracing, Replay) 71.47 KB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 76.78 KB - -
CDN Bundle - uncompressed 70.7 KB - -
CDN Bundle (incl. Tracing) - uncompressed 109.36 KB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 221.71 KB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 234.93 KB - -
@sentry/nextjs (client) 37.94 KB - -
@sentry/sveltekit (client) 35.6 KB - -
@sentry/node 124.9 KB - -
@sentry/node - without tracing 94.13 KB - -
@sentry/aws-serverless 103.7 KB - -

View base workflow run

@s1gr1d s1gr1d self-requested a review October 2, 2024 15:12
Copy link
Member

@s1gr1d s1gr1d left a comment

Choose a reason for hiding this comment

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

Thanks for adding this 🙌

@@ -16,6 +16,7 @@
},
"dependencies": {
"@sentry/vue": "latest || *",
"pinia": "^2.2.3",
Copy link
Member

Choose a reason for hiding this comment

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

Should we add pinia as a peer dependency as well with major v2?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sure, added pinia as an optional peer dependency. 👍

const options = client && client.getOptions();
const normalizationDepth = (options && options.normalizeDepth) || 3; // default state normalization depth to 3

const newStateContext = { state: { type: 'pinia', value: transformedState } };
Copy link
Member

Choose a reason for hiding this comment

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

H: Currently, the state context needs to be nested twice with state (docs here). This could change in the future as it is a bit confusing, but right now it still has to look that way to be understood by Sentry.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is assigned under store key in the context, so I guess it should comply with the current interface.

Copy link
Member

Choose a reason for hiding this comment

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

Ah right, I see it's there: scope.setContext('state', newStateContext);

Comment on lines 32 to 33
filename: 'pinia_state.json',
data: JSON.stringify(store.$state),
Copy link
Member

Choose a reason for hiding this comment

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

L: Can there be multiple files (because the linked event has two)? Users could benefit from having a timestamp in the filename as there can be multiple files in the attachments. Something like hh:mm_pinia_state would be enough.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Looks like these two are two different stores. So, I updated the file name format to include both the store id and the timestamp. Sample Event

Comment on lines 15 to 16
actionTransformer: (action: any) => any;
stateTransformer: (state: any) => any;
Copy link
Member

Choose a reason for hiding this comment

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

I think it would be nice to have tests for those :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added 👍 c7cfa72

...(hint.attachments || []),
{
filename,
data: JSON.stringify(store.$state),
Copy link
Member

Choose a reason for hiding this comment

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

M: What just came to my mind: the store can contain PII, especially when pinia is used in forms. What do you think of adding the type of the value as the value? Like this:

name: string
likes: object

Copy link
Member

Choose a reason for hiding this comment

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

Since the pinia plugin needs to be added manually I would argue it is fine. Just having the type is basically useless for debugging.

Copy link

Choose a reason for hiding this comment

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

Stringifying the state as JSON will only work in some scenarios and fail in many others like Dates, Errors, Maps, Sets, and custom classes. I would recommend using https://github.com/Rich-Harris/devalue like Nuxt does to allow users to provide serializer and revivers. I would even recommend adding a custom one that uses a toJSON() method if it exists by default as a convention. I think, but I'm not sure, that Nuxt has something similar.

Copy link
Member

Choose a reason for hiding this comment

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

Hi, we are on a tight bundle-size budget so we need to be a bit careful what we pull in as deps.

What we definitely should do is wrap the stringify call in a try-catch because it can throw in certain situations.

Non-serializable data is tricky and I would recommend we do it in a follow-up if at all. Dates get serialized properly with ordinary JSON.stringify, errors, maps, and sets, will become objects (which is fine for now), and stringify already supports toJSON natively.

Copy link

Choose a reason for hiding this comment

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

The toJSON() was specific to devalue ofc. If users can opt-in to a fully compatible serializable solution, they would be able to put their app in a similar state with the serialized state and debug in dev mode, so I think it's nice to advocate for that! Since Map and Sets are common, it would be a shame to lose them fully by using JSON.stringify()`

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

So, this event processor logic is already in try-catch, so we're safe on that part. We can do a follow-up for serializing. Thanks for the review @posva!

Comment on lines 13 to 16
type SentryPiniaPluginOptions = {
attachPiniaState?: boolean;
actionTransformer: (action: any) => any;
stateTransformer: (state: any) => any;
Copy link
Member

Choose a reason for hiding this comment

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

In addition to having the entire options object be optional, I think all of the options individually should probably also be optional.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Updated: c8482b9

...(hint.attachments || []),
{
filename,
data: JSON.stringify(store.$state),
Copy link
Member

Choose a reason for hiding this comment

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

Since the pinia plugin needs to be added manually I would argue it is fine. Just having the type is basically useless for debugging.

const transformedAction = options.actionTransformer(context.name);

if (typeof transformedAction !== 'undefined' && transformedAction !== null) {
addBreadcrumb({
Copy link
Member

Choose a reason for hiding this comment

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

I would add an option to turn off creating breadcrumbs. From experience, some apps have a lot of state updates and breadcrumbs might be very spammy.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Updated: ea7ba22


scope.setContext('state', newStateContext);
} else {
scope.setContext('state', null);
Copy link
Member

Choose a reason for hiding this comment

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

Is there a way for us not to nuke the entire state context here but only the pinia state 🤔 I know it could be tricky and is a bit edge-casy but may be worth it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Does this update make sense? 4a78c04

Copy link
Member

Choose a reason for hiding this comment

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

yes! perfect

Copy link
Member

@s1gr1d s1gr1d left a comment

Choose a reason for hiding this comment

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

LGTM 🙌

@s1gr1d s1gr1d merged commit ecf84e0 into develop Oct 15, 2024
147 checks passed
@s1gr1d s1gr1d deleted the onur/pinia-plugin branch October 15, 2024 08:44
@github-actions github-actions bot mentioned this pull request Oct 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Plugin for Pinia 🍍
4 participants