Skip to content

Commit

Permalink
Refactor datatable with Pagination Searchobject API service call
Browse files Browse the repository at this point in the history
Add Sort functionality to the datatable

Add filters to the datatable
  • Loading branch information
jatindersingh93 committed Feb 10, 2024
1 parent b7b227d commit 00a1128
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 117 deletions.
6 changes: 5 additions & 1 deletion frontend/src/components/object/ObjectFilters.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ const searching = ref(false);
const selectedMetadata: Ref<MetadataPair[]> = ref([]);
const selectedTags: Ref<Tag[]> = ref([]);
// Emits
const emit = defineEmits(['selectedFilters']);
// Store subscriptions
objectStore.$onAction(({ name, args }) => {
// If someone calls fetchObjects to refresh the table, clear the filter
Expand Down Expand Up @@ -118,7 +121,8 @@ const selectedFilterValuesChanged = () => {
// Get the 'display' property out from selected tag and metadata
const metaToSearch: Array<MetadataPair> = selectedMetadata.value.map(({ ...meta }: any) => meta);
const tagSetToSearch: Array<Tag> = selectedTags.value.map(({ ...tag }: any) => tag);
console.log(metaToSearch);

Check warning on line 124 in frontend/src/components/object/ObjectFilters.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

Unexpected console statement

Check warning on line 124 in frontend/src/components/object/ObjectFilters.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

Unexpected console statement

Check warning on line 124 in frontend/src/components/object/ObjectFilters.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

Unexpected console statement

Check warning on line 124 in frontend/src/components/object/ObjectFilters.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

Unexpected console statement

Check warning on line 124 in frontend/src/components/object/ObjectFilters.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

Unexpected console statement

Check warning on line 124 in frontend/src/components/object/ObjectFilters.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

Unexpected console statement
emit('selectedFilters', { metaToSearch, tagSetToSearch });
// Search the object store with the tagset as a param and metadata as headers
objectStore.fetchObjects(
{
Expand Down
194 changes: 128 additions & 66 deletions frontend/src/components/object/ObjectTable.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia';
import { onUnmounted, ref, watch } from 'vue';
import { onUnmounted, onMounted, ref } from 'vue';
import { Spinner } from '@/components/layout';
import {
Expand All @@ -12,11 +12,12 @@ import {
} from '@/components/object';
import { SyncButton } from '@/components/common';
import { ShareObjectButton } from '@/components/object/share';
import { Button, Column, DataTable, Dialog, FilterMatchMode, InputText, useToast } from '@/lib/primevue';
import { useAuthStore, useAppStore, useObjectStore, usePermissionStore } from '@/store';
import { Button, Column, DataTable, Dialog, FilterMatchMode, InputText, useToast, MultiSelect } from '@/lib/primevue';

Check warning on line 15 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

'FilterMatchMode' is defined but never used

Check warning on line 15 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

'MultiSelect' is defined but never used

Check warning on line 15 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

'FilterMatchMode' is defined but never used

Check warning on line 15 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

'MultiSelect' is defined but never used

Check warning on line 15 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

'FilterMatchMode' is defined but never used

Check warning on line 15 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

'MultiSelect' is defined but never used

Check warning on line 15 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

'FilterMatchMode' is defined but never used

Check warning on line 15 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

'MultiSelect' is defined but never used

Check warning on line 15 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

'FilterMatchMode' is defined but never used

Check warning on line 15 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

'MultiSelect' is defined but never used

Check warning on line 15 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

'FilterMatchMode' is defined but never used

Check warning on line 15 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

'MultiSelect' is defined but never used
import { useAuthStore, useObjectStore, usePermissionStore } from '@/store';
import { Permissions } from '@/utils/constants';
import { ButtonMode } from '@/utils/enums';
import { formatDateLong } from '@/utils/formatters';
import { objectService } from '@/services';
import type { Ref } from 'vue';
import type { COMSObject } from '@/types';
Expand All @@ -25,6 +26,10 @@ type COMSObjectDataSource = {
lastUpdatedDate?: string;
} & COMSObject;
type DataTableObjectSource = {
[key: string]: any;
};
// Props
type Props = {
bucketId?: string;
Expand All @@ -42,7 +47,6 @@ const emit = defineEmits(['show-object-info']);
// Store
const objectStore = useObjectStore();
const permissionStore = usePermissionStore();
const { getObjects } = storeToRefs(objectStore);
const { getUserId } = storeToRefs(useAuthStore());
// State
Expand All @@ -51,17 +55,26 @@ const permissionsVisible = ref(false);
const permissionsObjectId = ref('');
const permissionsObjectName: Ref<string | undefined> = ref('');
const tableData: Ref<Array<COMSObjectDataSource>> = ref([]);
const dt = ref();
const loading = ref(false);
const lazyParams: Ref<DataTableObjectSource> = ref({});
const totalRecords = ref(0);
const first = ref(0);
const selectedObjects: any = ref();
const filters = ref({
name: { value: undefined, matchMode: 'contains' },
tags: { value: undefined, matchMode: 'contains' },
meta: { value: undefined, matchMode: 'contains' }
});
// Actions
const toast = useToast();
function clearSelected() {
selectAll.value = false;
objectStore.setSelectedObjects([]);
}
const formatShortUuid = (uuid: string) => uuid?.slice(0, 8) ?? uuid;
const onDeletedSuccess = () => toast.success('File deleted');
const onDeletedSuccess = () => {
toast.success('File deleted');
loadLazyData();
};
function selectCurrentPage() {

Check warning on line 79 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

'selectCurrentPage' is defined but never used

Check warning on line 79 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

'selectCurrentPage' is defined but never used

Check warning on line 79 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

'selectCurrentPage' is defined but never used

Check warning on line 79 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

'selectCurrentPage' is defined but never used

Check warning on line 79 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

'selectCurrentPage' is defined but never used

Check warning on line 79 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

'selectCurrentPage' is defined but never used
objectStore.setSelectedObjects(
Expand All @@ -82,92 +95,142 @@ async function showPermissions(objectId: string) {
permissionsObjectId.value = objectId;
permissionsObjectName.value = objectStore.findObjectById(objectId)?.name;
}
onMounted(() => {
loading.value = true;
watch(getObjects, async () => {
clearSelected();
// Filter object cache to this specific bucket
const objs: Array<COMSObjectDataSource> = getObjects.value.filter(
(x: COMSObject) => x.bucketId === props.bucketId
) as COMSObjectDataSource[];
tableData.value = objs.map((x: COMSObjectDataSource) => {
x.lastUpdatedDate = x.updatedAt ?? x.createdAt;
return x;
});
lazyParams.value = {
first: 0,
rows: dt.value.rows,
sortField: 'updatedAt',
page: dt.value.page,
sortOrder: 'desc',
filters: filters
};
loadLazyData();
});
const loadLazyData = (event?: any) => {
lazyParams.value = { ...lazyParams.value, first: event?.first || first.value, page: event?.page || 0 };
objectService
.searchObjects(
{
bucketId: props.bucketId ? [props.bucketId] : undefined,
deleteMarker: false,
latest: true,
permissions: true,
page: lazyParams.value?.page ? ++lazyParams.value.page : 1,
name: lazyParams.value?.filters?.name.value,
limit: lazyParams.value.rows,
sort: lazyParams.value.sortField,
order: lazyParams.value.sortOrder === 1 ? 'asc' : 'desc',
tagset: lazyParams.value?.filters?.tags.value
},
lazyParams.value?.filters?.meta.value //Header
)
.then((r: any) => {
tableData.value = r.data;
totalRecords.value = +r?.headers['x-total-rows'];
loading.value = false;
});
};
const onPage = (event?: any) => {
lazyParams.value = event;
loadLazyData(event);
};
const onSort = (event?: any) => {
lazyParams.value = event;
loadLazyData(event);
};
const onFilter = (event?: any) => {
lazyParams.value.filters = filters;
// Seems to be a bug as current page is not being reset when filter trigger
dt.value.resetPage();
loadLazyData(event);
};
// Clear selections when navigating away
onUnmounted(() => {
objectStore.setSelectedObjects([]);
});
// Datatable filter(s)
const filters = ref({
// Need this till PrimeVue gets it together to un-break this again
// TODO: Revisit with PrimeVue 2.37+
// @ts-ignore
global: { value: null, matchMode: FilterMatchMode.CONTAINS }
});
const selectedFilters = (payload: any) => {
filters.value.meta.value = payload.metaToSearch
.flatMap((o: any) => {
return { [o.key]: o.value };
})
.reduce((r: any, c: any) => {
const key = Object.keys(c)[0];
const value = c[key];
r['x-amz-meta-' + key] = value;
return r;
}, {});
console.log(filters.value.meta.value);

Check warning on line 167 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

Unexpected console statement

Check warning on line 167 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

Unexpected console statement

Check warning on line 167 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

Unexpected console statement

Check warning on line 167 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

Unexpected console statement

Check warning on line 167 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

Unexpected console statement

Check warning on line 167 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

Unexpected console statement
filters.value.tags.value = payload.tagSetToSearch
.flatMap((o: any) => {
return { [o.key]: o.value };
})
.reduce((r: any, c: any) => {
const key = Object.keys(c)[0];
const value = c[key];
r[key] = value;
return r;
}, {});
lazyParams.value.filters = filters;
};
</script>

<template>
<div class="object-table">
<DataTable
v-model:selection="objectStore.selectedObjects"
ref="dt"
v-model:selection="selectedObjects"
v-model:filters="filters"
:loading="useAppStore().getIsLoading"
:value="tableData"
lazy
paginator
:loading="loading"
v-model:value="tableData"

Check warning on line 192 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

Attribute "v-model:value" should go before ":loading"

Check warning on line 192 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

Attribute "v-model:value" should go before ":loading"

Check warning on line 192 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

Attribute "v-model:value" should go before ":loading"

Check warning on line 192 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

Attribute "v-model:value" should go before ":loading"

Check warning on line 192 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

Attribute "v-model:value" should go before ":loading"

Check warning on line 192 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

Attribute "v-model:value" should go before ":loading"
:total-records="totalRecords"
data-key="id"
class="p-datatable-sm"
responsive-layout="scroll"
:paginator="true"
:rows="10"
paginator-template="RowsPerPageDropdown CurrentPageReport PrevPageLink NextPageLink "
current-page-report-template="{first}-{last} of {totalRecords}"
:rows="3"
:rows-per-page-options="[10, 20, 50]"
sort-field="lastUpdatedDate"
sort-field="updatedAt"
:sort-order="-1"
filterDisplay="row"

Check warning on line 201 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

Attribute 'filterDisplay' must be hyphenated

Check warning on line 201 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (18.x)

Attribute 'filterDisplay' must be hyphenated

Check warning on line 201 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

Attribute 'filterDisplay' must be hyphenated

Check warning on line 201 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (16.x)

Attribute 'filterDisplay' must be hyphenated

Check warning on line 201 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

Attribute 'filterDisplay' must be hyphenated

Check warning on line 201 in frontend/src/components/object/ObjectTable.vue

View workflow job for this annotation

GitHub Actions / Unit Tests (Frontend) (20.x)

Attribute 'filterDisplay' must be hyphenated
:global-filter-fields="['name']"
:select-all="selectAll"
@select-all-change="
(e) => {
selectAll = e.checked;
if (e.checked) {
selectCurrentPage();
} else {
objectStore.setSelectedObjects([]);
}
}
"
@page="clearSelected"
@update:sort-order="clearSelected"
@update:sort-field="clearSelected"
@update:rows="clearSelected"
:first="first"
update:filters
update:page
@page="onPage($event)"
@sort="onSort($event)"
@filter="onFilter($event)"
>
<template #header>
<div class="flex justify-content-end">
<ObjectFilters :bucket-id="props.bucketId" />
<ObjectFilters
:bucket-id="props.bucketId"
@selected-filters="selectedFilters"
/>

<span class="p-input-icon-left ml-4">
<i class="pi pi-search" />
<InputText
v-model="filters['global'].value"
v-model="filters.name.value"
class="searchInput"
placeholder="Search File Names"
@input="clearSelected()"
/>
<Button
v-show="filters['global'].value !== null"
v-show="filters.name.value !== undefined"
v-tooltip.bottom="'Clear'"
class="ml-2 p-input-icon-clear-right"
icon="pi pi-times"
outlined
aria-label="Clear"
@click="
() => {
filters['global'].value = null;
clearSelected();
filters.name.value = undefined;
}
"
/>
Expand All @@ -180,13 +243,13 @@ const filters = ref({
outlined
rounded
aria-label="Filter"
@click="objectStore.fetchObjects({ bucketId: props.bucketId, userId: getUserId, bucketPerms: true })"
@click="onFilter()"
/>
</div>
</template>
<template #empty>
<div
v-if="!useAppStore().getIsLoading"
v-if="!loading"
class="flex justify-content-center"
>
<h3>There are no objects associated with your account in this bucket.</h3>
Expand All @@ -201,7 +264,7 @@ const filters = ref({
/>
<Column
field="name"
:sortable="true"
sortable
header="Name"
header-style="min-width: 25%"
body-class="truncate"
Expand All @@ -214,7 +277,7 @@ const filters = ref({
</Column>
<Column
field="id"
:sortable="true"
sortable
header="Object ID"
style="width: 150px"
>
Expand All @@ -228,14 +291,13 @@ const filters = ref({
</template>
</Column>
<Column
field="lastUpdatedDate"
field="updatedAt"
header="Updated date"
style="width: 300px"
:sortable="true"
:hidden="props.objectInfoId ? true : false"
sortable
>
<template #body="{ data }">
{{ formatDateLong(data.lastUpdatedDate) }}
{{ formatDateLong(data.updatedAt ?? data.createdAt) }}
</template>
</Column>
<Column
Expand Down
51 changes: 1 addition & 50 deletions frontend/src/services/objectService.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import ConfigService from './configService';
import { comsAxios } from './interceptors';
import { excludeMetaTag, setDispositionHeader } from '@/utils/utils';

Expand Down Expand Up @@ -220,55 +219,7 @@ export default {
* @returns {Promise} An axios response
*/
async searchObjects(params: SearchObjectsOptions = {}, headers: any = {}) {
// remove objectId array if its first element is undefined
if (params.objectId && params.objectId[0] === undefined) delete params.objectId;

if (params.objectId) {
/**
* split calls to COMS if query params (eg objectId's)
* will cause url length to excede 2000 characters
* see: https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers
*
* TODO: consider creating a utils function
* eg: `divideParam(params, attr)`
* ...
* return Promise.all(divideParam(params, objectId)
* .map(zparam => comsAxios().get(PATH, {params: zparam, headers: headers});
*/
let urlLimit = 2000;

const baseUrl = new URL(`${new ConfigService().getConfig().coms.apiPath}${PATH}`).toString();
urlLimit -= baseUrl.length; // subtract baseUrl length
if (params.deleteMarker) urlLimit -= 19; // subtract `deleteMarker=false`
if (params.latest) urlLimit -= 13; // subtract `latest=false`
if (params.bucketId) urlLimit -= 48; // subtract a single bucketId `bucketId[]=<uuid>`
// if tagset parameters passed
if (params.tagset) {
type TagsetObjectEntries = {
[K in keyof Tag]: [K, Tag[K]];
}[keyof Tag][];
for (const [key, value] of Object.entries(params.tagset) as TagsetObjectEntries) {
urlLimit -= 10 + key.length + value.length;
}
}

// number of allowed objectId's in each group - 48 chars for each objectId (&objectId[]=<uuid>)
const space = urlLimit;
const groupSize = Math.floor(space / 48);

// loop through each group and push COMS result to `groups` array
const iterations = Math.ceil(params.objectId.length / groupSize);
const groups = [];
for (let i = 0; i < iterations; i++) {
const ids = params.objectId.slice(i * groupSize, i * groupSize + groupSize);
groups.push(await comsAxios().get(PATH, { params: { ...params, objectId: ids }, headers: headers }));
}
return Promise.resolve({ data: groups.flatMap((result) => result.data) });
}
// else just call COMS once
else {
return comsAxios().get(PATH, { params: params, headers: headers });
}
return comsAxios().get(PATH, { params: params, headers: headers });
},

/**
Expand Down
Loading

0 comments on commit 00a1128

Please sign in to comment.