Web: use wrapper for the OpenAPI client to track requests
A wrapper for the generated `ApiClient` class tracks the number of running queries. This makes it much simpler to show the "API calls pending" UI element, regardless of which part of the webapp performs the queries.
This commit is contained in:
parent
fc68e8a413
commit
4129ed11bb
@ -1,19 +1,19 @@
|
||||
<template>
|
||||
<header>{{ flamencoName }}</header>
|
||||
<header class="right">
|
||||
<api-spinner :numRunningQueries="numRunningQueries" />
|
||||
<api-spinner />
|
||||
version: {{ flamencoVersion }}
|
||||
</header>
|
||||
<div class="col-1">
|
||||
<jobs-table ref="jobsTable" :apiClient="apiClient" @selectedJobChange="onSelectedJobChanged" />
|
||||
<jobs-table ref="jobsTable" @selectedJobChange="onSelectedJobChanged" />
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<job-details :apiClient="apiClient" :jobData="selectedJob" />
|
||||
<job-details :jobData="selectedJob" />
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<task-details :apiClient="apiClient" />
|
||||
<task-details />
|
||||
</div>
|
||||
<footer>Footer
|
||||
<footer>
|
||||
<span class='notifications' v-if="notifs.last">{{ notifs.last.msg }}</span>
|
||||
<update-listener ref="updateListener" :websocketURL="websocketURL" @jobUpdate="onSioJobUpdate"
|
||||
@message="onChatMessage" @sioReconnected="onSIOReconnected" @sioDisconnected="onSIODisconnected" />
|
||||
@ -25,6 +25,7 @@ import * as urls from '@/urls'
|
||||
import * as API from '@/manager-api';
|
||||
import { useJobs } from '@/stores/jobs';
|
||||
import { useNotifs } from '@/stores/notifications';
|
||||
import { apiClient } from '@/stores/api-query-count';
|
||||
|
||||
import ApiSpinner from '@/components/ApiSpinner.vue'
|
||||
import JobsTable from '@/components/JobsTable.vue'
|
||||
@ -41,7 +42,6 @@ export default {
|
||||
ApiSpinner, JobsTable, JobDetails, TaskDetails, UpdateListener,
|
||||
},
|
||||
data: () => ({
|
||||
apiClient: new API.ApiClient(urls.api()),
|
||||
websocketURL: urls.ws(),
|
||||
messages: [],
|
||||
|
||||
@ -50,8 +50,6 @@ export default {
|
||||
|
||||
flamencoName: DEFAULT_FLAMENCO_NAME,
|
||||
flamencoVersion: DEFAULT_FLAMENCO_VERSION,
|
||||
|
||||
numRunningQueries: 0,
|
||||
}),
|
||||
mounted() {
|
||||
window.app = this;
|
||||
@ -68,8 +66,8 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
const jobsAPI = new API.JobsApi(this.apiClient);
|
||||
this._wrap(jobsAPI.fetchJob(jobSummary.id))
|
||||
const jobsAPI = new API.JobsApi(apiClient);
|
||||
jobsAPI.fetchJob(jobSummary.id)
|
||||
.then((job) => {
|
||||
this.jobs.setSelectedJob(job);
|
||||
// Forward the full job to Tabulator, so that that gets updated too.
|
||||
@ -122,21 +120,12 @@ export default {
|
||||
this.flamencoVersion = DEFAULT_FLAMENCO_VERSION;
|
||||
},
|
||||
fetchManagerInfo() {
|
||||
const metaAPI = new API.MetaApi(this.apiClient);
|
||||
const metaAPI = new API.MetaApi(apiClient);
|
||||
metaAPI.getVersion().then((version) => {
|
||||
this.flamencoName = version.name;
|
||||
this.flamencoVersion = version.version;
|
||||
})
|
||||
},
|
||||
|
||||
// Wrap a Flamenco API promise, to keep track of how many queries are running.
|
||||
// This is just a test to see how this works, not a final functional design.
|
||||
_wrap(promise) {
|
||||
this.numRunningQueries++;
|
||||
return promise.finally(() => {
|
||||
this.numRunningQueries--;
|
||||
});
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -1,10 +1,14 @@
|
||||
<template>
|
||||
<span class="api-spinner" :class="{ running: numRunningQueries > 0 }">🐈</span>
|
||||
<span class="api-spinner" :class="{ running: apiQueryCount.num > 0 }">🐈</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useAPIQueryCount } from '@/stores/api-query-count';
|
||||
|
||||
export default {
|
||||
props: ["numRunningQueries"],
|
||||
data: () => ({
|
||||
apiQueryCount: useAPIQueryCount(),
|
||||
}),
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -68,6 +68,7 @@
|
||||
<script lang="js">
|
||||
import * as datetime from "@/datetime";
|
||||
import * as API from '@/manager-api';
|
||||
import { apiClient } from '@/stores/api-query-count';
|
||||
|
||||
function objectEmpty(o) {
|
||||
if (!o) return true;
|
||||
@ -77,14 +78,13 @@ window.objectEmpty = objectEmpty;
|
||||
|
||||
export default {
|
||||
props: [
|
||||
"apiClient", // Flamenco Manager API client.
|
||||
"jobData", // Job data to show.
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
datetime: datetime, // So that the template can access it.
|
||||
simpleSettings: null, // Object with filtered job settings, or null if there is no job.
|
||||
jobsApi: new API.JobsApi(this.apiClient),
|
||||
jobsApi: new API.JobsApi(apiClient),
|
||||
jobType: null, // API.AvailableJobType object for the current job type.
|
||||
jobTypeSettings: null, // Mapping from setting key to its definition in the job type.
|
||||
showAllSettings: false,
|
||||
|
@ -9,11 +9,12 @@
|
||||
import { TabulatorFull as Tabulator } from 'tabulator-tables';
|
||||
import * as datetime from "@/datetime";
|
||||
import * as API from '@/manager-api'
|
||||
import { apiClient } from '@/stores/api-query-count';
|
||||
|
||||
import JobActionsBar from '@/components/JobActionsBar.vue'
|
||||
|
||||
export default {
|
||||
emits: ["selectedJobChange"],
|
||||
props: ["apiClient"],
|
||||
components: {
|
||||
JobActionsBar,
|
||||
},
|
||||
@ -71,10 +72,7 @@ export default {
|
||||
tab.setSort(tab.getSorters()); // This triggers re-sorting.
|
||||
},
|
||||
fetchAllJobs() {
|
||||
if (!this.apiClient) {
|
||||
throw "no apiClient set on JobsTable component";
|
||||
}
|
||||
const jobsApi = new API.JobsApi(this.apiClient);
|
||||
const jobsApi = new API.JobsApi(apiClient);
|
||||
const jobsQuery = {};
|
||||
jobsApi.queryJobs(jobsQuery).then(this.onJobsFetched, function (error) {
|
||||
// TODO: error handling.
|
||||
@ -127,6 +125,11 @@ export default {
|
||||
.job-list-container {
|
||||
font-family: 'Noto Mono', monospace;
|
||||
font-size: smaller;
|
||||
height: calc(100% - var(--action-bar-height));
|
||||
height: calc(100% - var(--action-bar-height) - 2em);
|
||||
}
|
||||
|
||||
.job-list {
|
||||
outline: 2px solid lime;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
</style>
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
<script lang="js">
|
||||
export default {
|
||||
props: ["apiClient"],
|
||||
data: () => {
|
||||
return {
|
||||
};
|
||||
|
46
web/app/src/stores/api-query-count.js
Normal file
46
web/app/src/stores/api-query-count.js
Normal file
@ -0,0 +1,46 @@
|
||||
import { defineStore } from "pinia";
|
||||
import * as API from "@/manager-api";
|
||||
import * as urls from '@/urls'
|
||||
|
||||
/**
|
||||
* Keep track of running API queries.
|
||||
*/
|
||||
export const useAPIQueryCount = defineStore("apiQueryCount", {
|
||||
state: () => ({
|
||||
/**
|
||||
* Number of running queries.
|
||||
*/
|
||||
num: 0,
|
||||
}),
|
||||
actions: {
|
||||
/**
|
||||
* Track this promise, counting it as a query for the spinner.
|
||||
* @param {Promise} promise
|
||||
*/
|
||||
async track(promise) {
|
||||
this.num++;
|
||||
try {
|
||||
return await promise;
|
||||
} finally {
|
||||
this.num--;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export class CountingApiClient extends API.ApiClient {
|
||||
callApi(path, httpMethod, pathParams, queryParams, headerParams, formParams,
|
||||
bodyParam, authNames, contentTypes, accepts, returnType, apiBasePath ) {
|
||||
const apiQueryCount = useAPIQueryCount();
|
||||
apiQueryCount.num++;
|
||||
|
||||
return super
|
||||
.callApi(path, httpMethod, pathParams, queryParams, headerParams, formParams,
|
||||
bodyParam, authNames, contentTypes, accepts, returnType, apiBasePath)
|
||||
.finally(() => {
|
||||
apiQueryCount.num--;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const apiClient = new CountingApiClient(urls.api());;
|
@ -2,8 +2,9 @@ import { defineStore } from 'pinia'
|
||||
|
||||
import * as urls from '@/urls'
|
||||
import * as API from '@/manager-api';
|
||||
import { apiClient } from '@/stores/api-query-count';
|
||||
|
||||
|
||||
const apiClient = new API.ApiClient(urls.api());
|
||||
const jobsAPI = new API.JobsApi(apiClient);
|
||||
|
||||
// 'use' prefix is idiomatic for Pinia stores.
|
||||
|
Loading…
x
Reference in New Issue
Block a user