Skip to content

Commit

Permalink
merge: Merge pull request #84 from ArunaStorage/82-location-aware-dow…
Browse files Browse the repository at this point in the history
…nload

Add alternative downloadlinks to objects
  • Loading branch information
das-Abroxas authored Jul 17, 2024
2 parents 1981d67 + 98cffc5 commit 079b8f4
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 9 deletions.
90 changes: 90 additions & 0 deletions components/card/downloads.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<script setup lang="ts">
import {IconCloudDown, IconCloudCancel, IconCloudCheck, IconCloudCog, IconCloudPause, IconCloudQuestion} from '@tabler/icons-vue';
import {type EndpointInfo, fetchEndpoint} from '~/composables/api_wrapper';
import { storagemodelsv2ReplicationStatus } from '../../composables/aruna_api_json';
const props = defineProps<{
endpoints: EndpointInfo[] | undefined
}>();
const emit = defineEmits<{(e: 'download', endpointId: string): void}>();
const endpointData = {};
await Promise.all(props.endpoints.map(async (endpointInfo: EndpointInfo) => {
try {
endpointData[endpointInfo.id] = await fetchEndpoint(endpointInfo.id);
} catch (error) {
console.log(error.code);
console.log(error.message);
}
}));
function toReplicationStatusIcon(variant: storagemodelsv2ReplicationStatus | undefined): string {
switch (variant) {
case storagemodelsv2ReplicationStatus.REPLICATION_STATUS_ERROR: return IconCloudCancel
case storagemodelsv2ReplicationStatus.REPLICATION_STATUS_FINISHED: return IconCloudCheck
case storagemodelsv2ReplicationStatus.REPLICATION_STATUS_RUNNING: return IconCloudCog
case storagemodelsv2ReplicationStatus.REPLICATION_STATUS_WAITING: return IconCloudPause
default: return IconCloudQuestion
}
}
function toReplicationStatusColor(variant: storagemodelsv2ReplicationStatus | undefined): string {
switch (variant) {
case storagemodelsv2ReplicationStatus.REPLICATION_STATUS_ERROR: return "red"
case storagemodelsv2ReplicationStatus.REPLICATION_STATUS_FINISHED: return "green"
case storagemodelsv2ReplicationStatus.REPLICATION_STATUS_RUNNING: return "orange"
case storagemodelsv2ReplicationStatus.REPLICATION_STATUS_WAITING: return "orange"
default: return "orange"
}
}
</script>

<template>
<div class="-m-1.5 overflow-x-auto">
<div class="p-1.5 min-w-full inline-block align-middle">
<div class="overflow-hidden">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead>
<tr>
<th scope="col" class="px-6 py-3 text-start text-sm font-medium text-gray-500 uppercase">
Endpoint Name
</th>
<th scope="col" class="px-6 py-3 text-start text-sm font-medium text-gray-500 uppercase">
Replication Status
</th>
<th scope="col" class="px-6 py-3 text-start text-sm font-medium text-gray-500 uppercase">
<center>Download</center>
</th>
</tr>
</thead>

<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
<tr v-for="endpoint in props.endpoints" class="hover:bg-gray-100 dark:hover:bg-gray-700">
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-700 dark:text-gray-200">
{{ endpointData[endpoint.id].name }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-700 dark:text-gray-200 flex item-center gap-2">
<span class="">{{ toReplicationStatusStr(endpoint.status) }}</span>
<component :is="toReplicationStatusIcon(endpoint.status)" class="flex-shrink-0" :color="toReplicationStatusColor(endpoint.status)"></component>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-700 dark:text-gray-200">
<center>
<button
type="button"
title="Download Object"
@click="emit('download', endpoint.id)"
:disabled="endpoint.status != storagemodelsv2ReplicationStatus.REPLICATION_STATUS_FINISHED"
class="inline-flex grow justify-center font-semibold rounded-lg border border-transparent text-gray-600 dark:text-white hover:bg-gray-50 disabled:opacity-50 disabled:pointer-events-none">
<IconCloudDown class="flex-shrink-0"/>
</button>
</center>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
2 changes: 1 addition & 1 deletion composables/api_wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
type v2SearchResourcesResponse,
type v2User
} from "./aruna_api_json"
import {type ObjectInfo, toObjectInfo} from "~/composables/proto_conversions";
import {type ObjectInfo, type EndpointInfo, toObjectInfo} from "~/composables/proto_conversions";
import type {ArunaError} from "~/composables/ArunaError";


Expand Down
11 changes: 11 additions & 0 deletions composables/enum_conversions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
modelsv2Status,
storagemodelsv2ComponentStatus,
storagemodelsv2ReplicationStatus,
v2AnnouncementType,
v2DataClass,
v2EndpointVariant,
Expand Down Expand Up @@ -113,4 +114,14 @@ export function toRelationDirectionStr(variant: v2RelationDirection | undefined)
case v2RelationDirection.RELATION_DIRECTION_OUTBOUND: return "Outbound"
default: return 'Unspecified'
}
}

export function toReplicationStatusStr(variant: storagemodelsv2ReplicationStatus | undefined): string {
switch (variant) {
case storagemodelsv2ReplicationStatus.REPLICATION_STATUS_WAITING: return "Waiting"
case storagemodelsv2ReplicationStatus.REPLICATION_STATUS_RUNNING: return "Running"
case storagemodelsv2ReplicationStatus.REPLICATION_STATUS_FINISHED: return "Finshed"
case storagemodelsv2ReplicationStatus.REPLICATION_STATUS_ERROR: return "Error"
default: return 'Unspecified'
}
}
16 changes: 11 additions & 5 deletions composables/proto_conversions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
v2Stats,
} from '@/composables/aruna_api_json';
import {v2ResourceVariant} from "@/composables/aruna_api_json";
import {storagemodelsv2ReplicationStatus} from './aruna_api_json';

/* Object info conversions */
export type ObjectInfo = {
Expand All @@ -27,7 +28,12 @@ export type ObjectInfo = {
permission: v2PermissionLevel,
license: string,
data_license: string,
endpoints: string[]
endpoints: EndpointInfo[]
}

export type EndpointInfo = {
id?: string,
status?: storagemodelsv2ReplicationStatus
}

export function toObjectInfo(
Expand Down Expand Up @@ -55,7 +61,7 @@ export function toObjectInfo(
permission: permission,
license: resource.project.metadataLicenseTag,
data_license: resource.project.defaultDataLicenseTag,
endpoints: resource.project.endpoints?.map(x => x.id)
endpoints: resource.project.endpoints?.map(x => ({id: x.id, status: x.status} as EndpointInfo))
} as ObjectInfo
} else if (resource.collection) {
return {
Expand All @@ -74,7 +80,7 @@ export function toObjectInfo(
permission: permission,
license: resource.collection.metadataLicenseTag,
data_license: resource.collection.defaultDataLicenseTag,
endpoints: resource.collection.endpoints?.map(x => x.id)
endpoints: resource.collection.endpoints?.map(x => ({id: x.id, status: x.status} as EndpointInfo))
} as ObjectInfo
} else if (resource.dataset) {
return {
Expand All @@ -93,7 +99,7 @@ export function toObjectInfo(
permission: permission,
license: resource.dataset.metadataLicenseTag,
data_license: resource.dataset.defaultDataLicenseTag,
endpoints: resource.dataset.endpoints?.map(x => x.id)
endpoints: resource.dataset.endpoints?.map(x => ({id: x.id, status: x.status} as EndpointInfo))
} as ObjectInfo
} else if (resource.object) {
return {
Expand All @@ -116,7 +122,7 @@ export function toObjectInfo(
permission: permission,
license: resource.object.metadataLicenseTag,
data_license: resource.object.dataLicenseTag,
endpoints: resource.object.endpoints?.map(x => x.id)
endpoints: resource.object.endpoints?.map(x => ({id: x.id, status: x.status} as EndpointInfo))
} as ObjectInfo
}

Expand Down
21 changes: 18 additions & 3 deletions pages/objects/[id].vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,15 @@ function isDownloadable(): boolean {
return false
}
async function downloadResource() {
async function downloadResource(endpointId?: string) {
if (objectInfo) {
if (typeof endpointId === "undefined") {
endpointId = objectInfo.endpoints[0].id
}
if (objectInfo.variant === v2ResourceVariant.RESOURCE_VARIANT_OBJECT) {
if (objectInfo.data_class === v2DataClass.DATA_CLASS_PUBLIC) {
//TODO: Choose nearest endpoint from object locations
const endpoint = await fetchEndpoint(objectInfo.endpoints[0])
const endpoint = await fetchEndpoint(endpointId)
const data_module = endpoint?.hostConfigs?.find(conf => conf.hostVariant === v2EndpointHostVariant.ENDPOINT_HOST_VARIANT_S3)
if (data_module?.url) {
Expand Down Expand Up @@ -86,7 +89,6 @@ async function downloadResource() {
} else {
// Create presigned download url for temp bundle
//TODO: Evaluate "nearest" DataProxy
let endpointId = objectInfo.endpoints[0]
// Fetch S3 credentials (includes host url)
const creds = await getUserS3Credentials(endpointId)
// Create S3 client and pre-sign url
Expand Down Expand Up @@ -252,6 +254,19 @@ const router = useRouter()
</div>
</div>
<!-- End Relations Row -->

<!-- Locations -->
<div v-if="isDownloadable()" class="flex flex-wrap justify-center gap-x-4 gap-y-2 container mx-auto mb-6">
<div
class="flex flex-col grow p-2 bg-white/[.5] border border-gray-400 dark:bg-slate-900 dark:border-gray-700 dark:text-gray-400">
<div class="flex flex-row justify-start items-center p-4 font-bold text-xl">
<IconCloudDown class="flex-shrink-0 size-6 me-4"/>
<span class="">Locations</span>
</div>
<CardDownloads :endpoints="objectInfo?.endpoints" @download="downloadResource"/>
</div>
</div>
<!-- End Locations -->
</div>
<div v-else class="">
<div class="flex flex-wrap justify-center container mx-auto mb-6">
Expand Down

0 comments on commit 079b8f4

Please sign in to comment.