client/main.js (352 lines of code) (raw):
// 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 Vue from 'vue';
import Router from 'vue-router';
import infiniteScroll from 'vue-infinite-scroll';
import vueModal from 'vue-js-modal';
import vueSplit from 'vue-split-panel';
import qs from 'friendly-querystring';
import promiseFinally from 'promise.prototype.finally';
import copyButton from './components/copy';
import snapscroll from './directives/snapscroll';
import App from './App';
import Domain from './routes/domain';
import DomainSearch from './routes/domain-search';
import DomainMetrics from './routes/domain/domain-metrics';
import DomainSettings from './routes/domain/domain-settings';
import Help from './routes/help';
import News from './routes/news';
import Query from './routes/workflow/query';
import Root from './routes';
import StackTrace from './routes/workflow/stack-trace';
import TaskList from './routes/task-list';
import TaskListMetrics from './routes/task-list/metrics';
import TaskListPartition from './routes/task-list/partition';
import TaskListPollers from './routes/task-list/pollers';
import WorkflowArchival from './routes/domain/workflow-archival';
import WorkflowArchivalAdvanced from './routes/domain/workflow-archival/advanced';
import WorkflowArchivalBasic from './routes/domain/workflow-archival/basic';
import WorkflowSummary from './routes/workflow/summary';
import initStore from './store';
import {
Workflow,
WorkflowHistory,
WorkflowList,
WorkflowPending,
} from '~containers';
import { injectMomentDurationFormat, jsonTryParse } from '~helpers';
const routeOpts = {
mode: 'history',
routes: [
{
path: '/',
redirect: '/domains',
component: Root,
children: [
{
name: 'domain-search',
path: '/domains',
components: {
'domain-search': DomainSearch,
},
},
{
name: 'help',
path: '/help',
components: {
help: Help,
},
},
{
name: 'news',
path: '/news/:year?/:month?/:date?/:article?',
components: {
news: News,
},
props: {
news: ({ params: { article, date, month, year } }) => ({
article,
date,
month,
year,
}),
},
},
],
},
{
name: 'domain',
path: '/domains/:domain/:clusterName?',
redirect: '/domains/:domain/:clusterName?/workflows',
component: Domain,
props: ({ params }) => ({
clusterName: params.clusterName,
domain: params.domain,
}),
children: [
{
name: 'workflow-list',
path: '/domains/:domain/:clusterName?/workflows',
components: {
'workflow-list': WorkflowList,
},
},
{
name: 'domain-metrics',
path: '/domains/:domain/:clusterName?/metrics',
components: {
'domain-metrics': DomainMetrics,
},
},
{
name: 'domain-settings',
path: '/domains/:domain/:clusterName?/settings',
components: {
'domain-settings': DomainSettings,
},
},
{
name: 'workflow-archival',
path: '/domains/:domain/:clusterName?/archival',
redirect: '/domains/:domain/:clusterName?/archival/basic',
components: {
'workflow-archival': WorkflowArchival,
},
children: [
{
name: 'workflow-archival-advanced',
path: '/domains/:domain/:clusterName?/archival/advanced',
components: {
'workflow-archival-advanced': WorkflowArchivalAdvanced,
},
},
{
name: 'workflow-archival-basic',
path: '/domains/:domain/:clusterName?/archival/basic',
components: {
'workflow-archival-basic': WorkflowArchivalBasic,
},
},
],
},
],
},
{
name: 'workflow',
path: '/domains/:domain/:clusterName?/workflows/:workflowId/:runId',
component: Workflow,
props: ({ params }) => ({
clusterName: params.clusterName,
displayWorkflowId: params.workflowId,
domain: params.domain,
runId: params.runId,
workflowId: encodeURIComponent(params.workflowId),
}),
children: [
{
name: 'workflow/summary',
path:
'/domains/:domain/:clusterName?/workflows/:workflowId/:runId/summary',
components: {
summary: WorkflowSummary,
},
props: {
summary: ({ params }) => ({
clusterName: params.clusterName,
runId: params.runId,
workflowId: encodeURIComponent(params.workflowId),
}),
},
},
{
name: 'workflow/history',
path:
'/domains/:domain/:clusterName?/workflows/:workflowId/:runId/history',
components: {
history: WorkflowHistory,
},
props: {
history: ({ params, query }) => ({
clusterName: params.clusterName,
domain: params.domain,
eventId: Number(query.eventId) || undefined,
format: query.format,
runId: params.runId,
showGraph: Boolean(query.showGraph) === true,
graphView: query.graphView,
workflowId: encodeURIComponent(params.workflowId),
}),
},
},
{
name: 'workflow/pending',
path:
'/domains/:domain/:clusterName?/workflows/:workflowId/:runId/pending',
components: {
pending: WorkflowPending,
},
props: {
pending: ({ params }) => ({
clusterName: params.clusterName,
}),
},
},
{
name: 'workflow/stack-trace',
path:
'/domains/:domain/:clusterName?/workflows/:workflowId/:runId/stack-trace',
components: {
stacktrace: StackTrace,
},
props: {
stacktrace: ({ params }) => ({
clusterName: params.clusterName,
}),
},
},
{
name: 'workflow/query',
path:
'/domains/:domain/:clusterName?/workflows/:workflowId/:runId/query',
components: {
query: Query,
},
props: {
query: ({ params }) => ({
clusterName: params.clusterName,
}),
},
},
],
},
{
name: 'task-list',
path: '/domains/:domain/:clusterName?/task-lists/:taskList',
redirect: '/domains/:domain/:clusterName?/task-lists/:taskList/pollers',
component: TaskList,
props: ({ params }) => ({
clusterName: params.clusterName,
domain: params.domain,
taskList: params.taskList,
}),
children: [
{
name: 'task-list/pollers',
path: '/domains/:domain/:clusterName?/task-lists/:taskList/pollers',
components: {
pollers: TaskListPollers,
},
},
{
name: 'task-list/partition',
path: '/domains/:domain/:clusterName?/task-lists/:taskList/partition',
components: {
partition: TaskListPartition,
},
},
{
name: 'task-list/metrics',
path: '/domains/:domain/:clusterName?/task-lists/:taskList/metrics',
components: {
partition: TaskListMetrics,
},
},
],
},
// redirects
{
name: 'domains-redirect',
path: '/domain/*',
redirect: '/domains/*',
},
{
name: 'domain-config-redirect',
path: '/domains/:domain/:clusterName?/config',
redirect: '/domains/:domain/:clusterName?/settings',
},
{
path: '/domains/:domain/:clusterName?/history',
redirect: ({ params, query }) => {
if (!query.runId || !query.workflowId) {
return {
name: 'workflow-list',
params,
};
}
const { runId, workflowId, ...queryWhitelist } = query;
const newParams = {
clusterName: params.clusterName,
runId,
workflowId,
domain: params.domain,
};
return {
name: 'workflow/history',
params: newParams,
query: queryWhitelist,
};
},
},
],
parseQuery: qs.parse.bind(qs),
stringifyQuery: query => {
const q = qs.stringify(query);
return q ? `?${q}` : '';
},
};
const router = new Router(routeOpts);
Object.getPrototypeOf(router).replaceQueryParam = function replaceQueryParam(
prop,
val
) {
const newQuery = {
...this.currentRoute.query,
[prop]: val,
};
if (!newQuery[prop]) {
delete newQuery[prop];
}
this.replace({ query: newQuery });
};
injectMomentDurationFormat();
JSON.tryParse = jsonTryParse;
promiseFinally.shim();
Vue.use(Router);
Vue.use(infiniteScroll);
Vue.use(vueModal, {
dialog: true,
dynamic: true,
});
Vue.use(vueSplit);
Vue.component('copy', copyButton);
Vue.directive('snapscroll', snapscroll);
Vue.config.ignoredElements = ['loader'];
if (typeof mocha === 'undefined') {
if (!document.querySelector('main')) {
document.body.appendChild(document.createElement('main'));
}
const store = initStore({ router });
// eslint-disable-next-line no-new
new Vue({
el: 'main',
router,
store,
template: '<App/>',
components: { App },
});
if (module.hot) {
module.hot.addStatusHandler(status => {
if (status === 'apply') {
document
.querySelectorAll('link[href][rel=stylesheet]')
.forEach(link => {
const nextStyleHref = link.href.replace(
/(\?\d+)?$/,
`?${Date.now()}`
);
// eslint-disable-next-line no-param-reassign
link.href = nextStyleHref;
});
}
});
}
}
export default {
App,
routeOpts,
};