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

expose setErrorMessageHandler #11694

Merged
merged 8 commits into from
Mar 19, 2024
Merged

expose setErrorMessageHandler #11694

merged 8 commits into from
Mar 19, 2024

Conversation

phryneas
Copy link
Member

This would be an alternative solution to the concerns brought up in #11667 and #11655.

@phryneas phryneas requested a review from a team as a code owner March 18, 2024 17:06
Copy link

changeset-bot bot commented Mar 18, 2024

🦋 Changeset detected

Latest commit: 5bc6188

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@apollo/client Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

if (!global[ApolloErrorMessageHandler]) {
global[ApolloErrorMessageHandler] = handler as typeof handler & ErrorCodes;
}
setErrorMessageHandler(handler as typeof handler & ErrorCodes);
Copy link
Member Author

Choose a reason for hiding this comment

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

This is a notable change (but probably not very relevant):

If a user had created their own handler, and then called loadErrorMessageHandler, previously that would not have overwritten that handler, and now it would.

We have never exposed a way of setting that handler before, though, so it probably doesn't matter.

Copy link
Contributor

github-actions bot commented Mar 18, 2024

size-limit report 📦

Path Size
dist/apollo-client.min.cjs 38.35 KB (0%)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/main.cjs" 46.17 KB (0%)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/main.cjs" (production) 43.73 KB (0%)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/index.js" 33.95 KB (0%)
import { ApolloClient, InMemoryCache, HttpLink } from "dist/index.js" (production) 31.87 KB (0%)
import { ApolloProvider } from "dist/react/index.js" 1.23 KB (0%)
import { ApolloProvider } from "dist/react/index.js" (production) 1.22 KB (0%)
import { useQuery } from "dist/react/index.js" 5.26 KB (0%)
import { useQuery } from "dist/react/index.js" (production) 4.35 KB (0%)
import { useLazyQuery } from "dist/react/index.js" 5.5 KB (0%)
import { useLazyQuery } from "dist/react/index.js" (production) 4.58 KB (0%)
import { useMutation } from "dist/react/index.js" 3.51 KB (0%)
import { useMutation } from "dist/react/index.js" (production) 2.73 KB (0%)
import { useSubscription } from "dist/react/index.js" 3.19 KB (0%)
import { useSubscription } from "dist/react/index.js" (production) 2.38 KB (0%)
import { useSuspenseQuery } from "dist/react/index.js" 5.36 KB (0%)
import { useSuspenseQuery } from "dist/react/index.js" (production) 4.03 KB (0%)
import { useBackgroundQuery } from "dist/react/index.js" 4.83 KB (0%)
import { useBackgroundQuery } from "dist/react/index.js" (production) 3.49 KB (0%)
import { useLoadableQuery } from "dist/react/index.js" 4.98 KB (0%)
import { useLoadableQuery } from "dist/react/index.js" (production) 3.63 KB (0%)
import { useReadQuery } from "dist/react/index.js" 3.12 KB (0%)
import { useReadQuery } from "dist/react/index.js" (production) 3.06 KB (0%)
import { useFragment } from "dist/react/index.js" 2.27 KB (0%)
import { useFragment } from "dist/react/index.js" (production) 2.22 KB (0%)

(msg, arg) => msg.replace(/%[sdfo]/, String(arg)),
String(message)
);
const handler = ((message: string | number, args: unknown[]) => {
Copy link
Member Author

Choose a reason for hiding this comment

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

Another thing to note is that this is now on module level, so anything that has loaded into it with loadErrorMessageHandler will now stay inside of it, and it cannot just be reset by delete global[ApolloErrorMessageHandler] - this might bite us in our tests, (let's see), but probably nobody else.

Copy link
Member

@jerelmiller jerelmiller left a comment

Choose a reason for hiding this comment

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

I dig it. Thanks for getting this up so quick!

@github-actions github-actions bot added the auto-cleanup 🤖 label Mar 18, 2024
Copy link

netlify bot commented Mar 19, 2024

Deploy Preview for apollo-client-docs ready!

Name Link
🔨 Latest commit 3d8b7aa
🔍 Latest deploy log https://app.netlify.com/sites/apollo-client-docs/deploys/65f95f83ef9e5e0008b18bb4
😎 Deploy Preview https://deploy-preview-11694--apollo-client-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link

netlify bot commented Mar 19, 2024

Deploy Preview for apollo-client-docs ready!

Name Link
🔨 Latest commit 5018738
🔍 Latest deploy log https://app.netlify.com/sites/apollo-client-docs/deploys/65f97ffd1f749a00089c86b3
😎 Deploy Preview https://deploy-preview-11694--apollo-client-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@phryneas
Copy link
Member Author

@iiroj Mentioning so you see this - it will give you full control over error messages and log messages.

This might, in the case of invariant.log etc. still log cache internals to the console (see the code comment on args), but will allow you to control the "url" behaviour that you want to avoid.

On top of that, you can control the logging of that using setVerbosity, so I hope these two things hand-in-hand should address all your concerns.

@phryneas phryneas merged commit 835d5f3 into main Mar 19, 2024
12 of 19 checks passed
@github-actions github-actions bot mentioned this pull request Mar 19, 2024
@iiroj
Copy link

iiroj commented Mar 20, 2024

@phryneas Thanks! I'm pretty sure this would allow me to override the message by supplying something like:

setErrorMessageHandler(() => "")

However, all of the code in the invariantWrappers.ts generates the error message like this, so any falsy values will fall through to the "fallback message":

getHandledErrorMsg(message, optionalParams) || getFallbackErrorMsg(message, optionalParams)

Also, since in the wrap function the final originalInvariant will be called with ...[message].concat(args), any customized message will still be followed by all the arguments (so basically the custom error message data).

I would expect setErrorMessageHandler(myHandler) to behave like this:

  • if myHandler = () => undefined, nothing gets logged to the console because the message is undefined
  • if myHandler = () => "An Apollo Client error occurred!", then the only thing that gets logged is that string value, and nothing else (the invariant error message arguments).

@jerelmiller jerelmiller deleted the pr/setErrorMessageHandler branch March 20, 2024 07:00
@phryneas
Copy link
Member Author

phryneas commented Mar 20, 2024

@iiroj I have documented both of these behaviours in the ErrorMessageHandler type:

/**
   * @returns The error message to be logged or thrown. If it returns `undefined`,
   *          the mechanism will fall back to the default:
   *          A link to https://go.apollo.dev/c/err with Apollo Client version,
   *          the error message number, and the error message arguments encoded into
   *          the URL hash.
   */

=> Returning undefined is the way of saying "we don't have a known error message for this, handle it the default way". That's important for us because you can load some of the messages (e.g. only the dev messages, or only the errors) and we still have to handle the rest somehow.

/**
   * ⚠️ Note that arguments will only be passed in for error messages.
   * For normal log messages, you will get an empty array here and they will directly
   * be passed to `console.log` instead, to have the string subsitution done by the
   * engine, as that allows for nicer (and in the case of a browser, interactive)
   * output.
   */

This is something that's also kind of a tradeoff:
If we wouldn't send the arguments for invariant.log directly to console.log, but we'd inline them into the string, they couldn't be explored using the browser console, which would hurt the ability to interactively inspect/collapse/debug those values.
We have to inline them in the error message, since JavaScript doesn't offer an "inspection" method like that for errors, though.

That said, invariant.debug/log/warn/error messages will never make it into the production build (assuming you have set that up correctly), so you should not have to worry about those in the first place.

(Take a look at the packaged JavaScript - a call to invariant.warn(e) would end up in our dist folder as globalThis.__DEV__ !== false && invariant.warn(e);, which would then be removed by your bundler in a prod build.)

In a production build, you'll only see InvariantErrors, and their messages are completely under your control.

If you even want to silence those logs in a development environment, completely (although I'd really recommend logging something!) you can still use ts-invariant's setVerbosity.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 20, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants