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

Improve errors #1656

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open

Improve errors #1656

wants to merge 10 commits into from

Conversation

flevi29
Copy link
Collaborator

@flevi29 flevi29 commented May 22, 2024

Pull Request

Related issues

Fixes #1612, #1655

What does this PR do?

This PR aims to improve errors, so that they can contain all the necessary information, and more.

  • Prevent browser test jsdom from replacing fetch and AbortController for consistency with node tests, replacing previous solution where we removed the builtin fetch from node tests
  • Remove "abort-controller" package, it was only used in tests and now AbortController is always available
  • Rename MeiliSearchCommunicationError to MeiliSearchRequestError, as this error might not be entirely related to communication, but rather the request itself
  • Make errors use cause, preserving the original error, simplifying things, taking advantage of modern browsers and runtimes actually printing this property
  • Remove the use of Object.setPrototypeOf in errors, this is not needed in modern browsers, and bundlers take care of it if we need to support older browsers (so in UMD bundle it's done twice currently). (https://stackoverflow.com/a/76851585)
  • Remove the use of Error.captureStackTrace, this is done by the base Error constructor, and it's only available in V8 engine based browsers/runtimes
  • Only catch the error from fetch to re-throw it as MeiliSearchRequestError, other potential errors should propagate as they're either truly unexpected or are thrown by us, simplifying error handling and not putting unexpected errors under the MeiliSearchError umbrella
  • Rename MeiliSearchErrorInfo type to MeiliSearchErrorResponse
  • Other minor changes/improvements

NOTE: Tests are horrifying, I didn't change all that much in src, but I had to change almost every test and by quite a bit. Testing is what I should aim to improve ASAP.

Copy link

codecov bot commented May 22, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 96.93%. Comparing base (7b2054d) to head (d3f490f).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1656      +/-   ##
==========================================
- Coverage   97.43%   96.93%   -0.51%     
==========================================
  Files          22       21       -1     
  Lines         858      815      -43     
  Branches       86       78       -8     
==========================================
- Hits          836      790      -46     
- Misses         21       24       +3     
  Partials        1        1              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@flevi29 flevi29 linked an issue May 22, 2024 that may be closed by this pull request
@flevi29 flevi29 requested a review from brunoocasali May 23, 2024 07:25
@flevi29
Copy link
Collaborator Author

flevi29 commented May 23, 2024

Breaking changes:

  • Rename MeiliSearchCommunicationError to MeiliSearchRequestError
  • Rename MeiliSearchErrorInfo type to MeiliSearchErrorResponse
  • MeiliSearchApiError now has a different property structure
    • MeiliSearchErrorResponse is now in its cause property
    • Instead of saving the HTTP status on the error, we save the whole Response on its response property for more available information (one of which is status, so error.response.status)
  • MeiliSearchRequestError now contains the whole caught error from fetch on its cause property instead of plucking some properties off of this error (this old way potentially losing information as we can not know the exact shape of this error)

@flevi29 flevi29 added the breaking-change The related changes are breaking for the users label May 23, 2024
@flevi29
Copy link
Collaborator Author

flevi29 commented May 25, 2024

I've looked into meilisearch-js-plugins, there is no handling of these special errors there, this should not break it in any way.

Comment on lines -30 to -69
test(`Not throw MeiliSearchCommunicationError when throwned error is MeiliSearchApiError`, async () => {
fetchMock.mockReject(
new MeiliSearchApiError(
{
message: 'Some error',
code: 'some_error',
type: 'random_error',
link: 'a link',
},
404,
),
);

const client = new MeiliSearch({ host: 'http://localhost:9345' });
try {
await client.health();
} catch (e: any) {
expect(e.name).toEqual('MeiliSearchApiError');
}
});

test('MeiliSearchApiError can be compared with the instanceof operator', async () => {
fetchMock.mockReject(
new MeiliSearchApiError(
{
message: 'Some error',
code: 'some_error',
type: 'random_error',
link: 'a link',
},
404,
),
);

const client = new MeiliSearch({ host: 'http://localhost:9345' });
try {
await client.health();
} catch (e: any) {
expect(e instanceof MeiliSearchApiError).toEqual(true);
}
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 test case was removed, because it's no longer applicable, MeiliSearchApiError doesn't get caught anymore to be re-thrown unnecessarily.

Comment on lines +99 to +100
expect(response).toHaveProperty('hits');
expect(Array.isArray(response.hits)).toBe(true);
Copy link
Collaborator Author

@flevi29 flevi29 May 29, 2024

Choose a reason for hiding this comment

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

Old way was checking with instanceof internally, but it is clearly sated in the docs that arrays are not to be checked this way: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray#description
With builtin Node.js fetch this was failing with the old way.

@flevi29
Copy link
Collaborator Author

flevi29 commented May 29, 2024

These changes might appear daunting at first, but it's mostly tests, and because tests are repeated ad nauseam with very little generalization, you've seen 3 types of changes and you've seen them all.

Comment on lines +167 to +169
const responseBody = await response.text();
const parsedResponse =
responseBody === '' ? undefined : JSON.parse(responseBody);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Previously if response.json() failed, we swallowed the error. There are cases when the response body is empty, and is parsed to an empty string initially. That is an invalid JSON. This case was handled by simply swallowing the error, now it's handled explicitly, and if for whatever reason Meilisearch returns something non-JSON-valid other than an empty body, it will err, which is what we want, as that's entirely unexpected and should propagate.

@@ -56,11 +56,11 @@ describe.each([
apiKey: key,
requestConfig: {
headers: {
Expect: '200-OK',
'Hello-There!': 'General Kenobi',
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@flevi29
Copy link
Collaborator Author

flevi29 commented Jun 1, 2024

Well, actually some more details about the cause property. Runtimes seem to print it and play nicely.

image

However, browsers don't give a damn. Only Firefox prints it at this moment, and unless cause is another Error object with a message property, it's not going to be of any service to you, absolutely beautiful work here:

image

But there's still hope, chromium is working on it: https://issues.chromium.org/issues/40182832
Maybe they will have the right ideas, unlike Firefox, even though they are massively late to the party, and let's not even talk about Safari, I'm sure they haven't yet started working on it.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause#browser_compatibility

Still, it's not like the old errors we have had any of their bespoke properties printed, so it's still an improvement, and it's future-proof.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking-change The related changes are breaking for the users
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Fetch error handling is incorrect problematic tests regarding Node.js built-in fetch
1 participant