client/routes/domain/workflow-archival/basic.vue (309 lines of code) (raw):
<script>
// Copyright (c) 2017-2024 Uber Technologies Inc.
//
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import debounce from 'lodash-es/debounce';
import { ArchivalTable, ArchivalTableRow } from './components';
import { ARCHIVAL_STATUS_LIST, LOADING_MESSAGE_DELAY } from './constants';
import {
getQueryParams,
getRange,
getStatus,
getStatusValue,
updateQueryFromRange,
mapArchivedWorkflowResponse,
} from './helpers';
import WorkflowArchivalService from './workflow-archival-service';
import pagedGrid from '~components/paged-grid';
import {
ButtonFill,
DateRangePicker,
ErrorMessage,
FlexGrid,
FlexGridItem,
LoadingMessage,
LoadingSpinner,
NoResults,
SelectInput,
TextInput,
} from '~components';
import {
getEndTimeIsoString,
getErrorMessage,
getStartTimeIsoString,
} from '~helpers';
export default pagedGrid({
name: 'workflow-archival-basic',
props: ['clusterName', 'dateFormat', 'domain', 'timeFormat', 'timezone'],
data() {
return {
error: undefined,
filterBy: 'CloseTime',
loading: false,
loadingMessageDelay: LOADING_MESSAGE_DELAY,
nextPageToken: undefined,
npt: undefined,
results: undefined,
statusList: ARCHIVAL_STATUS_LIST,
};
},
computed: {
endTime() {
const { range } = this;
const { endTime } = this.$route.query || {};
return getEndTimeIsoString(range, endTime);
},
formattedResults() {
const { dateFormat, results, timeFormat, timezone } = this;
return mapArchivedWorkflowResponse({
dateFormat,
results,
timeFormat,
timezone,
});
},
queryParams() {
const {
endTime,
workflowName,
statusValue,
startTime,
workflowId,
} = this;
return getQueryParams({
endTime,
workflowName,
statusValue,
startTime,
workflowId,
});
},
range() {
const { endTime, range, startTime } = this.$route.query || {};
return getRange({ endTime, range, startTime });
},
workflowName() {
const { workflowName = '' } = this.$route.query || {};
return workflowName;
},
status() {
const statusValue = this.$route.query && this.$route.query.status;
const { statusList } = this;
return getStatus({ statusList, statusValue });
},
statusValue() {
const { status, statusList } = this;
return getStatusValue({ status, statusList });
},
startTime() {
const { range } = this;
const { startTime } = this.$route.query || {};
return getStartTimeIsoString(range, startTime);
},
workflowId() {
const { workflowId = '' } = this.$route.query || {};
return workflowId;
},
},
created() {
const { domain, queryParams } = this;
this.workflowArchivalService = WorkflowArchivalService({ domain });
this.onQueryChange({ ...queryParams, nextPageToken: undefined });
},
methods: {
resetState() {
this.error = undefined;
this.loading = false;
this.results = undefined;
this.npt = undefined;
this.nextPageToken = undefined;
},
fetchArchivalRecord: debounce(async function fetchArchivalRecord(
queryParams
) {
if (!queryParams || !queryParams.startTime || !queryParams.endTime) {
return;
}
this.loading = true;
try {
const {
results,
nextPageToken,
} = await this.workflowArchivalService.fetchArchivalRecords(
queryParams
);
this.results = this.results ? this.results.concat(results) : results;
this.npt = nextPageToken;
} catch (error) {
if (error.name === 'AbortError') {
return;
}
this.npt = undefined;
this.error = getErrorMessage(error);
}
this.loading = false;
},
200),
onDateRangeChange(updatedRange) {
const { query } = this.$route;
const updatedQuery = updateQueryFromRange({ query, updatedRange });
this.$router.replace({ query: updatedQuery });
},
onQueryChange(queryParams) {
this.resetState();
if (queryParams) {
this.fetchArchivalRecord({ ...queryParams, nextPageToken: undefined });
}
},
onNextTokenChange(queryParams) {
this.fetchArchivalRecord(queryParams);
},
onTextChange({ target: { name, value } }) {
this.setQueryParam(name, value.trim());
},
onSelectChange({ value }) {
this.setQueryParam('status', value);
},
setQueryParam(name, value) {
this.$router.replaceQueryParam(name, value);
},
},
watch: {
queryParams(queryParams) {
this.onQueryChange(queryParams);
},
nextPageToken(nextPageToken) {
const { queryParams } = this;
if (nextPageToken && queryParams) {
this.onNextTokenChange({ ...queryParams, nextPageToken });
}
},
},
components: {
'archival-table': ArchivalTable,
'archival-table-row': ArchivalTableRow,
'button-fill': ButtonFill,
'date-range-picker': DateRangePicker,
'error-message': ErrorMessage,
'flex-grid': FlexGrid,
'flex-grid-item': FlexGridItem,
'loading-message': LoadingMessage,
'loading-spinner': LoadingSpinner,
'no-results': NoResults,
'select-input': SelectInput,
'text-input': TextInput,
},
});
</script>
<template>
<section class="workflow-archival-basic">
<header>
<flex-grid>
<flex-grid-item grow="1">
<text-input
label="Workflow ID"
name="workflowId"
type="search"
:value="workflowId"
@input="onTextChange"
/>
</flex-grid-item>
<flex-grid-item grow="1">
<text-input
label="Workflow Name"
name="workflowName"
type="search"
:value="workflowName"
@input="onTextChange"
/>
</flex-grid-item>
<flex-grid-item width="160px">
<select-input
label="Status"
:options="statusList"
:value="status"
@change="onSelectChange"
/>
</flex-grid-item>
<flex-grid-item width="165px">
<text-input label="Filter by" readonly :value="filterBy" />
</flex-grid-item>
<flex-grid-item width="325px">
<date-range-picker :date-range="range" @change="onDateRangeChange" />
</flex-grid-item>
<flex-grid-item width="120px">
<button-fill
label="ADVANCED"
tag="router-link"
:to="{
name: 'workflow-archival-advanced',
params: { clusterName },
}"
/>
</flex-grid-item>
</flex-grid>
</header>
<section class="results">
<archival-table
v-infinite-scroll="nextPage"
infinite-scroll-disabled="disableInfiniteScroll"
infinite-scroll-distance="20"
infinite-scroll-immediate-check="false"
>
<archival-table-row
v-for="result in formattedResults"
:close-status="result.closeStatus"
:close-time="result.closeTime"
:cluster-name="clusterName"
:domain-name="result.domainName"
:key="result.runId"
:run-id="result.runId"
:start-time="result.startTime"
:workflow-id="result.workflowId"
:workflow-name="result.workflowName"
/>
</archival-table>
<error-message :error="error" />
<no-results :results="results" />
<loading-spinner v-if="loading" />
<loading-message :delay="loadingMessageDelay" :loading="loading">
<p>It looks like this request is taking some time to process.</p>
<p>Try narrowing the time range to improve search time.</p>
</loading-message>
</section>
</section>
</template>
<style lang="stylus">
section.workflow-archival-basic {
display: flex;
flex: 1 1 auto;
flex-direction: column;
overflow-y: auto;
header {
padding: 16px;
}
section.results {
margin-top: 16px;
min-height: 100px;
overflow: auto;
}
paged-grid();
}
</style>