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

Allow non-JSON responses in miqFetch (add options.skipJsonParsing) #6645

Merged
merged 1 commit into from
Feb 3, 2020

Conversation

mturley
Copy link
Contributor

@mturley mturley commented Jan 30, 2020

Needed for https://bugzilla.redhat.com/show_bug.cgi?id=1788730

In RedHatCloudForms/cfme-migration_analytics#37, we are adding a new API endpoint which delivers a binary response (content-disposition: attachment) intended for the UI to make available as a file download. Rationale for the decision to handle the file download via the API instead of a UI worker can be found in the relevant discussions at RedHatCloudForms/cfme-migration_analytics#19 and RedHatCloudForms/cfme-migration_analytics#33.

Currently, the API methods provided here (which we must use to handle authentication) assume API responses will always be JSON, and call response.json() as part of processing the fetch() response. This causes an error when trying to make a call to the new endpoint, since it is returning binary gzip data.

This PR adds a new option to miqFetch called skipJsonParsing which simply skips that .json() call, instead resolving the promise with the raw response object directly. This gives the calling function in the UI access to response.blob() and response.headers(), which are necessary for reading the binary data and attachment filename.

To test these changes, you can run the following code in a browser's JavaScript console:

API.get('/api', { skipJsonParsing: true }).then(response => console.log({ response }));

Links

BZ for backport to ivanchuk: https://bugzilla.redhat.com/show_bug.cgi?id=1788730
JIRA task for feature: https://issues.redhat.com/browse/MIGENG-241
Issue for feature: RedHatCloudForms/cfme-migration_analytics#19
Dependent PR: RedHatCloudForms/cfme-migration_analytics#37
Initial closed PR with implementation context: RedHatCloudForms/cfme-migration_analytics#33

@mturley
Copy link
Contributor Author

mturley commented Jan 30, 2020

@miq-bot add_label ivanchuk/yes

@mturley
Copy link
Contributor Author

mturley commented Jan 30, 2020

@miq-bot add_label enhancement

@himdel
Copy link
Contributor

himdel commented Jan 30, 2020

Looks good, except.. is skipJsonParsing an actual fetch option, or just a new miqFetch option?

If the latter, we should make sure it doesn't end up in fetchOpts .. there's a bunch of deletes in processOptions, can you add it there please?

@himdel
Copy link
Contributor

himdel commented Jan 30, 2020

And as the code is, the option will get ignored for non-200 responses.

If we want to support those as well, maybe something like..

  if (response.status === 204) {
    // No content
    return Promise.resolve(null);
  }

  const maybeJson = options.skipJsonParsing ? (r) => r.json() : (r) => r;    // <--

  if (response.status >= 300) {
    // Not 1** or 2**
    // clone() because otherwise if json() fails, you can't call text()
    return maybeJson(response.clone())    // <--
      .catch(tryHtmlError(response))
      .then(rejectWithData(response));
  }

  return maybeJson(response);    // <--

?

(and possibly a fix for error modal not being able to meaningfully display the response, but not sure that's really possible for binary responses anyway :))

@himdel
Copy link
Contributor

himdel commented Jan 30, 2020

Oh, one more idea (just an idea really :)).. do we need an option?

Because, theoretically the data is already available in the responses content-type header, right?

No harm in being explicit though.

@mturley
Copy link
Contributor Author

mturley commented Jan 30, 2020

Looks good, except.. is skipJsonParsing an actual fetch option, or just a new miqFetch option?

The latter.

If the latter, we should make sure it doesn't end up in fetchOpts .. there's a bunch of deletes in processOptions, can you add it there please?

I already did 😄

And as the code is, the option will get ignored for non-200 responses.

That's an interesting point, but it seems like (at least with the 404 I tried: /api/red_hat_migration_analytics_payload?task_id=12345) non-200 responses will return JSON to describe the error, even if the endpoint returns non-JSON content on success.

Oh, one more idea (just an idea really :)).. do we need an option?
Because, theoretically the data is already available in the responses content-type header, right?

That's true... are you suggesting I just resolve with the raw response like this if response.headers('content-disposition') includes 'attachment' (or content-type is application/binary)? I was considering having it automatically call response.blob() in that case, so the promise always resolved to a response body (instead of the entire Response meta-object). But since my UI code also needs to be able to access that header to get the filename from it, I thought it would be nice to have an explicit way to skip any kind of body parsing and make it clear that the promise resolves to a Response. Maybe the option would be better named rawResponse or something.

@mturley
Copy link
Contributor Author

mturley commented Jan 30, 2020

To be clear, I think it would be ideal if the miqFetch promise always returns JSON when it is rejected, and the option only modifies the expected response type in the successful resolve case. That way I could reuse existing error handling code in the UI which expects JSON responses.

@miq-bot
Copy link
Member

miq-bot commented Jan 31, 2020

Checked commit mturley@d8428c0 with ruby 2.5.5, rubocop 0.69.0, haml-lint 0.20.0, and yamllint 1.10.0
0 files checked, 0 offenses detected
Everything looks fine. 🍪

@himdel
Copy link
Contributor

himdel commented Feb 3, 2020

Ah, perfect :)
OK, in that case this seems to match your use case, LGTM 👍

@himdel himdel merged commit a10fb37 into ManageIQ:master Feb 3, 2020
@himdel himdel self-assigned this Feb 3, 2020
@himdel himdel added this to the Sprint 129 Ending Feb 3, 2020 milestone Feb 3, 2020
@mturley
Copy link
Contributor Author

mturley commented Feb 3, 2020

Thanks @himdel !

@mturley mturley deleted the api-fetch-allow-non-json-responses branch February 6, 2020 19:55
simaishi pushed a commit that referenced this pull request Feb 21, 2020
Allow non-JSON responses in miqFetch (add `options.skipJsonParsing`)

(cherry picked from commit a10fb37)

https://bugzilla.redhat.com/show_bug.cgi?id=1805818
@simaishi
Copy link
Contributor

Ivanchuk backport details:

$ git log -1
commit 44b61f3c0cbf3c827cb57db6fc290758fd97aa7e
Author: Martin Hradil <mhradil@redhat.com>
Date:   Mon Feb 3 14:54:03 2020 +0100

    Merge pull request #6645 from mturley/api-fetch-allow-non-json-responses

    Allow non-JSON responses in miqFetch (add `options.skipJsonParsing`)

    (cherry picked from commit a10fb3702a2c8fe066390e9b3d5bec2a3889e2d7)

    https://bugzilla.redhat.com/show_bug.cgi?id=1805818

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