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

Support client-side exports and one-time export tokens #1240

Merged
merged 3 commits into from
Oct 24, 2022

Conversation

axelboc
Copy link
Contributor

@axelboc axelboc commented Oct 24, 2022

Fix #1063


Providers that implement getExportURL can now return an async function that gets called when the user actually clicks on an export entry. This function can return either Blob or URL to support two main use cases:

Note that this is not restrictive. Data provider implementations are free to do whatever they want - e.g.:

  • they can perform a client-side export by returning a data: URI instead of a Blob (with the risk of reaching the URL length limit);
  • they can make a GET request directly to an authenticated export endpoint (and return a Blob) instead of requesting/returning a one-time token.

Now to actually resolve #1063 and https://gitlab.esrf.fr/ui/daiquiri/-/merge_requests/538#note_195991, I've added a way for consumers of the providers to pass their own implementations of getExportURL.

* - `() => Promise<URL>` Provider generates single-use export URLs (i.e. signed one-time tokens)
* - `() => Promise<Blob>` Export is to be generated client-side
* - `undefined` Export scenario is not supported
*/
public getExportURL?<D extends Dataset<ArrayShape>>(
dataset: D,
selection: string | undefined,
value: Value<D>,
format: ExportFormat
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm tempted to move the format first. What do you think? If you agree, I'll do that in a separate PR.

packages/app/src/providers/mock/mock-api.ts Show resolved Hide resolved
async function handleBtnClick(getURLOrBlob: () => Promise<URL | Blob>) {
const anchor = document.createElement('a');
anchor.download = filename;
document.body.append(anchor);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

To start a download for which you don't know the URL right away, the idea is to add a temporary link to the DOM (invisibly), set its href once the URL is known, and click on it programmatically.

Copy link
Member

Choose a reason for hiding this comment

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

Wow, that is convoluted. Perhaps add a small comment to state the idea ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, let me know if it's clear.

Copy link
Member

Choose a reason for hiding this comment

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

Yep 👍

packages/app/src/providers/models.ts Show resolved Hide resolved
@axelboc
Copy link
Contributor Author

axelboc commented Oct 24, 2022

In the future, we may want to improve the APIs of the provider components (especially H5GroveProvider), as well as the corresponding back-ends, to get one-time tokens/authenticated exports out of the box.

We can also consider bringing some client-side export utilities into @h5web/h5wasm.

However, in both cases, I would prefer to see consumer implementations first.

Copy link
Member

@loichuder loichuder left a comment

Choose a reason for hiding this comment

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

Promising 💯

packages/app/src/providers/mock/mock-api.ts Show resolved Hide resolved
async function handleBtnClick(getURLOrBlob: () => Promise<URL | Blob>) {
const anchor = document.createElement('a');
anchor.download = filename;
document.body.append(anchor);
Copy link
Member

Choose a reason for hiding this comment

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

Wow, that is convoluted. Perhaps add a small comment to state the idea ?

const label = `Export to ${format.toUpperCase()}`;
const filename = `data.${format}`;

async function handleBtnClick(getURLOrBlob: () => Promise<URL | Blob>) {
Copy link
Member

Choose a reason for hiding this comment

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

This function could be moved below the check url instanceof URL as it will not be used in this case

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Functions declarations are hoisted, so this won't do what you think 😉: https://developer.mozilla.org/en-US/docs/Glossary/Hoisting

@loichuder
Copy link
Member

However, in both cases, I would prefer to see consumer implementations first.

Agreed. All the bricks are there but we must first build something with them before deciding on the future abstractions.

@axelboc
Copy link
Contributor Author

axelboc commented Oct 24, 2022

/approve

@axelboc axelboc merged commit 0842f9b into main Oct 24, 2022
@axelboc axelboc deleted the client-side-export branch October 24, 2022 15:07
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.

Allow client-generated exports of datasets
2 participants