Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
a543a11
Add basic history model type alias from schema
davelopez Apr 18, 2023
519e97a
Add barebones HistoryArchiveWizard component prototype
davelopez Apr 18, 2023
3fadda7
Add barebones HistoryExportSelector component
davelopez Apr 18, 2023
40f4894
Tweak History Archive UI + refactor
davelopez Apr 21, 2023
fe2922c
Handle export record status
davelopez Apr 24, 2023
d24359c
Use historyStore for retrieving the history data
davelopez Apr 26, 2023
d23d9eb
Add Archived Histories list prototype
davelopez Apr 26, 2023
9fa11e5
Add database migration script
davelopez May 3, 2023
b4296c7
Add archive columns to History model
davelopez May 3, 2023
55f2db0
Serialize `archived` column in summary view
davelopez May 3, 2023
284be28
Refactor StoreExportTracker
davelopez May 5, 2023
fdbc84c
Refactor HistoryExportManager
davelopez May 5, 2023
db0eed8
Refactor OrderQueryParam for reuse
davelopez May 5, 2023
75bc8b9
Include `archived` status in history summary serialization
davelopez May 8, 2023
41ab1dd
Add basic API endpoints for archiving and listing
davelopez May 8, 2023
1fe28e8
Add API endpoint for restoring archived history
davelopez May 8, 2023
446750a
Add basic API tests without export record
davelopez May 9, 2023
cec1b49
Refactor history archival testing related actions
davelopez May 9, 2023
faa30df
Improve export record association checks
davelopez May 9, 2023
d53b0f4
Raise conflict when trying to re-archive
davelopez May 9, 2023
d4c99ef
Purge history after archival on demand
davelopez May 9, 2023
2c8848f
Update client API schema
davelopez May 9, 2023
25f26f1
Adapt/Fix HistoryNavigation unit tests
davelopez May 9, 2023
d2b00c2
Add basic golden path integration test
davelopez May 9, 2023
960ffa2
Increase test coverage + fixes
davelopez May 10, 2023
caae64a
Update client API schema
davelopez May 10, 2023
7785822
Remove unnecessary serialization params
davelopez May 10, 2023
d033227
Add archived history client queries
davelopez May 10, 2023
891f3ff
Do not list archived histories with regular ones
davelopez May 10, 2023
e0c8c2f
Allow to archive histories from the store
davelopez May 11, 2023
a555788
Implement archiving without export in UI
davelopez May 11, 2023
1ef532c
Support total_matches in archived histories listing
davelopez May 11, 2023
b93bc3f
Replace table with card items in HistoryArchive
davelopez May 11, 2023
23ae06d
Implement restoring non-purged archived histories
davelopez May 12, 2023
8d9afde
Implement archive with export record in UI
davelopez May 12, 2023
01312da
Implement reimport archive copy in UI
davelopez May 12, 2023
cc0986c
Small cosmetic enhancements
davelopez May 15, 2023
fbd1cd6
Fix error handling when updating export records
davelopez May 15, 2023
843036a
Allow unarchiving a purged history
davelopez May 15, 2023
bd72584
Add contents information for export record
davelopez May 16, 2023
e345ae7
Do not display upload on empty non writeable histories
davelopez May 16, 2023
f5e8afc
Make archived (and purged) history view readonly
davelopez May 16, 2023
74bcc3d
Do not display both options if there are no remote sources
davelopez May 17, 2023
ca97ec1
Add client tests for HistoryArchiveWizard
davelopez May 17, 2023
c61907b
Extract interface from ExportRecordModel
davelopez May 19, 2023
5bc933c
Refactor extract component ExportRecordCard
davelopez May 19, 2023
368e803
Add some unit tests for HistoryArchiveExportSelector
davelopez May 19, 2023
c4d3f8e
Display only keep storage option if celery is disabled
davelopez May 22, 2023
1dfccd1
Remove purged badge
davelopez May 24, 2023
8eda77b
Improve error handling on ApiError
davelopez May 24, 2023
2b70da6
Archive after purging
davelopez May 24, 2023
be0c38a
Merge history state info in the same alert
davelopez May 24, 2023
7e11c22
Refactor history archive client services
davelopez May 25, 2023
91cbb8e
additional information about benefits of archival
davelopez May 25, 2023
e089818
Make @typescript-eslint/ban-ts-comment a warning
davelopez May 25, 2023
c45c20e
Apply suggestions from code review
davelopez May 26, 2023
3583e8e
Use transaction instead of session begin
davelopez Jun 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions client/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ module.exports = {
rules: {
...baseRules,
"@typescript-eslint/no-throw-literal": "error",
"@typescript-eslint/ban-ts-comment": "warn",
},
parser: "@typescript-eslint/parser",
parserOptions: {
Expand Down
62 changes: 51 additions & 11 deletions client/src/components/Common/models/exportRecordModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,58 @@ type ExportObjectRequestMetadata = components["schemas"]["ExportObjectRequestMet
export type StoreExportPayload = components["schemas"]["StoreExportPayload"];
export type ObjectExportTaskResponse = components["schemas"]["ObjectExportTaskResponse"];

export class ExportParamsModel {
export interface ExportParams {
readonly modelStoreFormat: string;
readonly includeFiles: boolean;
readonly includeDeleted: boolean;
readonly includeHidden: boolean;
}

export interface ExportRecord {
readonly id: string;
readonly isReady: boolean;
readonly isPreparing: boolean;
readonly isUpToDate: boolean;
readonly hasFailed: boolean;
readonly date: Date;
readonly elapsedTime: string;
readonly taskUUID: string;
readonly importUri?: string;
readonly canReimport: boolean;
readonly stsDownloadId?: string;
readonly isStsDownload: boolean;
readonly canDownload: boolean;
readonly modelStoreFormat: string;
readonly exportParams?: ExportParams;
readonly duration?: number;
readonly canExpire: boolean;
readonly isPermanent: boolean;
readonly expirationDate?: Date;
readonly expirationElapsedTime?: string;
readonly hasExpired: boolean;
readonly errorMessage?: string;
}

export class ExportParamsModel implements ExportParams {
private _params: StoreExportPayload;
constructor(data: StoreExportPayload = {}) {
this._params = data;
}

get modelStoreFormat() {
return this._params?.model_store_format;
return this._params?.model_store_format ?? "tgz";
}

get includeFiles() {
return this._params?.include_files;
return Boolean(this._params?.include_files);
}

get includeDeleted() {
return this._params?.include_deleted;
return Boolean(this._params?.include_deleted);
}

get includeHidden() {
return this._params?.include_hidden;
return Boolean(this._params?.include_hidden);
}

public equals(otherExportParams?: ExportParamsModel) {
Expand All @@ -41,9 +73,9 @@ export class ExportParamsModel {
}
}

export class ExportRecordModel {
export class ExportRecordModel implements ExportRecord {
private _data: ObjectExportTaskResponse;
private _expirationDate?: Date | null;
private _expirationDate?: Date;
private _requestMetadata?: ExportObjectRequestMetadata;
private _exportParameters?: ExportParamsModel;

Expand All @@ -56,6 +88,10 @@ export class ExportRecordModel {
: undefined;
}

get id() {
return this._data.id;
}

get isReady() {
return (this._data.ready && !this.hasExpired) ?? false;
}
Expand Down Expand Up @@ -109,7 +145,7 @@ export class ExportRecordModel {
}

get modelStoreFormat() {
return this.exportParams?.modelStoreFormat;
return this.exportParams?.modelStoreFormat ?? "tgz";
}

get exportParams() {
Expand All @@ -125,21 +161,25 @@ export class ExportRecordModel {
return this.isStsDownload && Boolean(this.duration);
}

get isPermanent() {
return !this.canExpire;
}

get expirationDate() {
if (this._expirationDate === undefined) {
this._expirationDate = this.duration ? new Date(this.date.getTime() + this.duration * 1000) : null;
this._expirationDate = this.duration ? new Date(this.date.getTime() + this.duration * 1000) : undefined;
}
return this._expirationDate;
}

get expirationElapsedTime() {
return this.canExpire && this.expirationDate
? formatDistanceToNow(this.expirationDate, { addSuffix: true })
: null;
: undefined;
}

get hasExpired() {
return this.canExpire && this.expirationDate && Date.now() > this.expirationDate.getTime();
return Boolean(this.canExpire && this.expirationDate && Date.now() > this.expirationDate.getTime());
}

get errorMessage() {
Expand Down
37 changes: 34 additions & 3 deletions client/src/components/Common/models/testData/exportData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ export const FAILED_DOWNLOAD_RESPONSE: ObjectExportTaskResponse = {
};

export const FILE_SOURCE_STORE_RESPONSE: ObjectExportTaskResponse = {
id: "FAKE_RECENT_DOWNLOAD_ID",
id: "FAKE_FILE_SOURCE_EXPORT_ID",
ready: true,
preparing: false,
up_to_date: true,
up_to_date: false,
task_uuid: "35563335-e275-4520-80e8-885793279095",
create_time: RECENT_EXPORT_DATE,
export_metadata: {
Expand All @@ -103,6 +103,37 @@ export const FILE_SOURCE_STORE_RESPONSE: ObjectExportTaskResponse = {
},
};

export const RECENT_FILE_SOURCE_STORE_RESPONSE: ObjectExportTaskResponse = {
...FILE_SOURCE_STORE_RESPONSE,
id: "FAKE_RECENT_FILE_SOURCE_EXPORT_ID",
up_to_date: true,
};

export const FAILED_FILE_SOURCE_STORE_RESPONSE: ObjectExportTaskResponse = {
...FILE_SOURCE_STORE_RESPONSE,
id: "FAKE_FAILED_FILE_SOURCE_EXPORT_ID",
export_metadata: {
request_data: FAKE_FILE_SOURCE_REQUEST_DATA,
result_data: FAILED_EXPORT_RESULT_DATA,
},
};

export const IN_PROGRESS_FILE_SOURCE_STORE_RESPONSE: ObjectExportTaskResponse = {
...FILE_SOURCE_STORE_RESPONSE,
id: "FAKE_IN_PROGRESS_FILE_SOURCE_EXPORT_ID",
ready: false,
preparing: true,
export_metadata: {
request_data: FAKE_FILE_SOURCE_REQUEST_DATA,
result_data: undefined,
},
};

export const EXPIRED_STS_DOWNLOAD_RECORD = new ExportRecordModel(EXPIRED_STS_DOWNLOAD_RESPONSE);
export const FILE_SOURCE_STORE_RECORD = new ExportRecordModel(FILE_SOURCE_STORE_RESPONSE);
export const RECENT_STS_DOWNLOAD_RECORD = new ExportRecordModel(RECENT_STS_DOWNLOAD_RESPONSE);
export const FAILED_DOWNLOAD_RECORD = new ExportRecordModel(FAILED_DOWNLOAD_RESPONSE);

export const FILE_SOURCE_STORE_RECORD = new ExportRecordModel(FILE_SOURCE_STORE_RESPONSE);
export const RECENT_FILE_SOURCE_STORE_RECORD = new ExportRecordModel(RECENT_FILE_SOURCE_STORE_RESPONSE);
export const FAILED_FILE_SOURCE_STORE_RECORD = new ExportRecordModel(FAILED_FILE_SOURCE_STORE_RESPONSE);
export const IN_PROGRESS_FILE_SOURCE_STORE_RECORD = new ExportRecordModel(IN_PROGRESS_FILE_SOURCE_STORE_RESPONSE);
24 changes: 24 additions & 0 deletions client/src/components/History/Archiving/ExportRecordCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<script setup lang="ts">
import { BCard, BCardText } from "bootstrap-vue";
import IncludedBadge from "./IncludedBadge.vue";
import type { ExportRecord } from "@/components/Common/models/exportRecordModel";

const props = defineProps<{
exportRecord: ExportRecord;
}>();
</script>

<template>
<b-card id="export-record-ready">
<b-card-text>
<b>Exported {{ props.exportRecord.elapsedTime }}</b> on {{ props.exportRecord.date }}
</b-card-text>
<b-card-text v-if="props.exportRecord.exportParams">
<b>Contains datasets:</b>
<IncludedBadge item-name="active" :included="props.exportRecord.exportParams.includeFiles" />
<IncludedBadge item-name="hidden" :included="props.exportRecord.exportParams.includeHidden" />
<IncludedBadge item-name="deleted" :included="props.exportRecord.exportParams.includeDeleted" />
</b-card-text>
<b-card-text> <b>Stored in:</b> {{ props.exportRecord.importUri }} </b-card-text>
</b-card>
</template>
Loading