Skip to content

Commit 648ec79

Browse files
authored
Merge pull request #22895 from nuwang/objectstore-direct-download
Add direct-download (presigned url) redirects for object store datasets
2 parents d9243f2 + 439f5d2 commit 648ec79

25 files changed

Lines changed: 834 additions & 34 deletions

File tree

client/packages/api-client/src/schema/schema.ts

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,30 @@ export interface paths {
983983
patch?: never;
984984
trace?: never;
985985
};
986+
"/api/datasets/{history_content_id}/download": {
987+
parameters: {
988+
query?: never;
989+
header?: never;
990+
path?: never;
991+
cookie?: never;
992+
};
993+
/**
994+
* Downloads the dataset, redirecting to the object store when possible.
995+
* @description Downloads the whole dataset file. Clients must follow the 302 redirect this route may return.
996+
*/
997+
get: operations["download_api_datasets__history_content_id__download_get"];
998+
put?: never;
999+
post?: never;
1000+
delete?: never;
1001+
options?: never;
1002+
/**
1003+
* Returns download metadata (size, filename) for the dataset.
1004+
* @description Downloads the whole dataset file. Clients must follow the 302 redirect this route may return.
1005+
*/
1006+
head: operations["download_api_datasets__history_content_id__download_head"];
1007+
patch?: never;
1008+
trace?: never;
1009+
};
9861010
"/api/datasets/{history_content_id}/metadata_file": {
9871011
parameters: {
9881012
query?: never;
@@ -2529,6 +2553,30 @@ export interface paths {
25292553
patch?: never;
25302554
trace?: never;
25312555
};
2556+
"/api/histories/{history_id}/contents/{history_content_id}/download": {
2557+
parameters: {
2558+
query?: never;
2559+
header?: never;
2560+
path?: never;
2561+
cookie?: never;
2562+
};
2563+
/**
2564+
* Downloads the dataset, redirecting to the object store when possible.
2565+
* @description Downloads the whole dataset file. Clients must follow the 302 redirect this route may return.
2566+
*/
2567+
get: operations["history_contents_download_api_histories__history_id__contents__history_content_id__download_get"];
2568+
put?: never;
2569+
post?: never;
2570+
delete?: never;
2571+
options?: never;
2572+
/**
2573+
* Returns download metadata (size, filename) for the dataset.
2574+
* @description Downloads the whole dataset file. Clients must follow the 302 redirect this route may return.
2575+
*/
2576+
head: operations["history_contents_download_api_histories__history_id__contents__history_content_id__download_head"];
2577+
patch?: never;
2578+
trace?: never;
2579+
};
25322580
"/api/histories/{history_id}/contents/{history_content_id}/extra_files": {
25332581
parameters: {
25342582
query?: never;
@@ -9126,6 +9174,8 @@ export interface components {
91269174
description?: string | null;
91279175
/** Device */
91289176
device?: string | null;
9177+
/** Enable Direct Download */
9178+
enable_direct_download?: boolean | null;
91299179
/** Name */
91309180
name?: string | null;
91319181
/** Object Expires After Days */
@@ -25301,6 +25351,8 @@ export interface components {
2530125351
description?: string | null;
2530225352
/** Device */
2530325353
device?: string | null;
25354+
/** Enable Direct Download */
25355+
enable_direct_download?: boolean | null;
2530425356
/** Hidden */
2530525357
hidden: boolean;
2530625358
/** Name */
@@ -33827,6 +33879,105 @@ export interface operations {
3382733879
};
3382833880
};
3382933881
};
33882+
download_api_datasets__history_content_id__download_get: {
33883+
parameters: {
33884+
query?: {
33885+
/** @description The file extension when downloading the display data. Use the value `data` to let the server infer it from the data type. */
33886+
to_ext?: string | null;
33887+
};
33888+
header?: {
33889+
/** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */
33890+
"run-as"?: string | null;
33891+
};
33892+
path: {
33893+
/** @description The ID of the History Dataset. */
33894+
history_content_id: string;
33895+
};
33896+
cookie?: never;
33897+
};
33898+
requestBody?: never;
33899+
responses: {
33900+
/** @description Successful Response */
33901+
200: {
33902+
headers: {
33903+
[name: string]: unknown;
33904+
};
33905+
content?: never;
33906+
};
33907+
/** @description Redirect to a URL serving the dataset directly from the backing object store. Only returned for whole-file downloads when the dataset's object store has `enable_direct_download` set. */
33908+
302: {
33909+
headers: {
33910+
[name: string]: unknown;
33911+
};
33912+
content?: never;
33913+
};
33914+
/** @description Request Error */
33915+
"4XX": {
33916+
headers: {
33917+
[name: string]: unknown;
33918+
};
33919+
content: {
33920+
"application/json": components["schemas"]["MessageExceptionModel"];
33921+
};
33922+
};
33923+
/** @description Server Error */
33924+
"5XX": {
33925+
headers: {
33926+
[name: string]: unknown;
33927+
};
33928+
content: {
33929+
"application/json": components["schemas"]["MessageExceptionModel"];
33930+
};
33931+
};
33932+
};
33933+
};
33934+
download_api_datasets__history_content_id__download_head: {
33935+
parameters: {
33936+
query?: {
33937+
/** @description The file extension when downloading the display data. Use the value `data` to let the server infer it from the data type. */
33938+
to_ext?: string | null;
33939+
};
33940+
header?: {
33941+
/** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */
33942+
"run-as"?: string | null;
33943+
};
33944+
path: {
33945+
/** @description The ID of the History Dataset. */
33946+
history_content_id: string;
33947+
};
33948+
cookie?: never;
33949+
};
33950+
requestBody?: never;
33951+
responses: {
33952+
/** @description Successful Response */
33953+
200: {
33954+
headers: {
33955+
[name: string]: unknown;
33956+
};
33957+
content: {
33958+
"application/json": unknown;
33959+
};
33960+
};
33961+
/** @description Request Error */
33962+
"4XX": {
33963+
headers: {
33964+
[name: string]: unknown;
33965+
};
33966+
content: {
33967+
"application/json": components["schemas"]["MessageExceptionModel"];
33968+
};
33969+
};
33970+
/** @description Server Error */
33971+
"5XX": {
33972+
headers: {
33973+
[name: string]: unknown;
33974+
};
33975+
content: {
33976+
"application/json": components["schemas"]["MessageExceptionModel"];
33977+
};
33978+
};
33979+
};
33980+
};
3383033981
datasets__get_metadata_file: {
3383133982
parameters: {
3383233983
query: {
@@ -38973,6 +39124,107 @@ export interface operations {
3897339124
};
3897439125
};
3897539126
};
39127+
history_contents_download_api_histories__history_id__contents__history_content_id__download_get: {
39128+
parameters: {
39129+
query?: {
39130+
/** @description The file extension when downloading the display data. Use the value `data` to let the server infer it from the data type. */
39131+
to_ext?: string | null;
39132+
};
39133+
header?: {
39134+
/** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */
39135+
"run-as"?: string | null;
39136+
};
39137+
path: {
39138+
/** @description The ID of the History Dataset. */
39139+
history_content_id: string;
39140+
history_id: string | null;
39141+
};
39142+
cookie?: never;
39143+
};
39144+
requestBody?: never;
39145+
responses: {
39146+
/** @description Successful Response */
39147+
200: {
39148+
headers: {
39149+
[name: string]: unknown;
39150+
};
39151+
content?: never;
39152+
};
39153+
/** @description Redirect to a URL serving the dataset directly from the backing object store. Only returned for whole-file downloads when the dataset's object store has `enable_direct_download` set. */
39154+
302: {
39155+
headers: {
39156+
[name: string]: unknown;
39157+
};
39158+
content?: never;
39159+
};
39160+
/** @description Request Error */
39161+
"4XX": {
39162+
headers: {
39163+
[name: string]: unknown;
39164+
};
39165+
content: {
39166+
"application/json": components["schemas"]["MessageExceptionModel"];
39167+
};
39168+
};
39169+
/** @description Server Error */
39170+
"5XX": {
39171+
headers: {
39172+
[name: string]: unknown;
39173+
};
39174+
content: {
39175+
"application/json": components["schemas"]["MessageExceptionModel"];
39176+
};
39177+
};
39178+
};
39179+
};
39180+
history_contents_download_api_histories__history_id__contents__history_content_id__download_head: {
39181+
parameters: {
39182+
query?: {
39183+
/** @description The file extension when downloading the display data. Use the value `data` to let the server infer it from the data type. */
39184+
to_ext?: string | null;
39185+
};
39186+
header?: {
39187+
/** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */
39188+
"run-as"?: string | null;
39189+
};
39190+
path: {
39191+
/** @description The ID of the History Dataset. */
39192+
history_content_id: string;
39193+
history_id: string | null;
39194+
};
39195+
cookie?: never;
39196+
};
39197+
requestBody?: never;
39198+
responses: {
39199+
/** @description Successful Response */
39200+
200: {
39201+
headers: {
39202+
[name: string]: unknown;
39203+
};
39204+
content: {
39205+
"application/json": unknown;
39206+
};
39207+
};
39208+
/** @description Request Error */
39209+
"4XX": {
39210+
headers: {
39211+
[name: string]: unknown;
39212+
};
39213+
content: {
39214+
"application/json": components["schemas"]["MessageExceptionModel"];
39215+
};
39216+
};
39217+
/** @description Server Error */
39218+
"5XX": {
39219+
headers: {
39220+
[name: string]: unknown;
39221+
};
39222+
content: {
39223+
"application/json": components["schemas"]["MessageExceptionModel"];
39224+
};
39225+
};
39226+
};
39227+
};
3897639228
extra_files_history_api_histories__history_id__contents__history_content_id__extra_files_get: {
3897739229
parameters: {
3897839230
query?: never;

client/src/components/Dataset/DatasetDisplay.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ const { isAdmin } = storeToRefs(useUserStore());
3838
3939
const dataset = computed(() => getDataset(props.datasetId));
4040
const datasetUrl = computed(() => `/datasets/${props.datasetId}/display/`);
41-
const downloadUrl = computed(() => withPrefix(`${datasetUrl.value}?to_ext=${dataset.value?.file_ext}`));
41+
const downloadUrl = computed(() =>
42+
withPrefix(`/api/datasets/${props.datasetId}/download?to_ext=${dataset.value?.file_ext}`),
43+
);
4244
const isLoading = computed(() => isLoadingDataset(props.datasetId));
4345
const previewUrl = computed(() => `${datasetUrl.value}?preview=True`);
4446

client/src/components/Dataset/DatasetView.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ const iframeLoading = ref(true);
4343
4444
const dataset = computed(() => datasetStore.getDataset(props.datasetId));
4545
const loadError = computed(() => datasetStore.getDatasetError(props.datasetId));
46-
const downloadUrl = computed(() => withPrefix(`/datasets/${props.datasetId}/display`));
46+
const downloadUrl = computed(() => withPrefix(`/api/datasets/${props.datasetId}/download`));
4747
const headerState = computed(() => (headerCollapsed.value ? "closed" : "open"));
4848
4949
// Track datatype loading state

client/src/components/History/Content/Dataset/DatasetActions.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const rerunUrl = computed(() => {
5858
return prependPath(props.itemUrls.rerun!);
5959
});
6060
const downloadUrl = computed(() => {
61-
return prependPath(`api/datasets/${props.item.id}/display?to_ext=${props.item.extension}`);
61+
return prependPath(`api/datasets/${props.item.id}/download?to_ext=${props.item.extension}`);
6262
});
6363
6464
function onCopyLink() {

client/src/components/History/Content/Dataset/DatasetDownload.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ describe("DatasetDownload", () => {
3838
expect(foundItems).toBe(false);
3939
await wrapper.trigger("click");
4040
const emitted = wrapper.emitted()["on-download"];
41-
expect(emitted?.[0]?.[0]).toBe(`/api/datasets/item_id/display?to_ext=ext`);
41+
expect(emitted?.[0]?.[0]).toBe(`/api/datasets/item_id/download?to_ext=ext`);
4242
expect(emitted?.[1]?.[0]).toBe(`/api/datasets/item_id/metadata_file?metadata_file=a`);
4343
expect(emitted?.[2]?.[0]).toBe(`/api/datasets/item_id/metadata_file?metadata_file=b`);
44-
expect(emitted?.[3]?.[0]).toBe(`/api/datasets/item_id/display?to_ext=ext`);
44+
expect(emitted?.[3]?.[0]).toBe(`/api/datasets/item_id/download?to_ext=ext`);
4545
});
4646
});

client/src/components/History/Content/Dataset/DatasetDownload.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const metaDownloadUrl = computed(() => {
2626
return prependPath(`api/datasets/${props.item.id}/metadata_file?metadata_file=`);
2727
});
2828
const downloadUrl = computed(() => {
29-
return prependPath(`api/datasets/${props.item.id}/display?to_ext=${props.item.extension}`);
29+
return prependPath(`api/datasets/${props.item.id}/download?to_ext=${props.item.extension}`);
3030
});
3131
const downloadTitle = computed(() => {
3232
const size = props.item.file_size;

0 commit comments

Comments
 (0)