mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2024-11-10 08:57:25 +01:00
Start to fix errors with urql 4
This commit is contained in:
parent
bb20ed655a
commit
52738c7f8e
@ -5,8 +5,8 @@ import resolve from '@rollup/plugin-node-resolve';
|
||||
import terser from '@rollup/plugin-terser';
|
||||
import css from 'rollup-plugin-css-only';
|
||||
|
||||
// const production = !process.env.ROLLUP_WATCH;
|
||||
const production = false
|
||||
const production = !process.env.ROLLUP_WATCH;
|
||||
// const production = false
|
||||
|
||||
const plugins = [
|
||||
svelte({
|
||||
|
@ -4,12 +4,20 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import { init } from "./utils.js";
|
||||
import { Row, Col, Button, Icon, Table,
|
||||
Card, Spinner, InputGroup, Input, } from "sveltestrap";
|
||||
import {
|
||||
Row,
|
||||
Col,
|
||||
Button,
|
||||
Icon,
|
||||
Table,
|
||||
Card,
|
||||
Spinner,
|
||||
InputGroup,
|
||||
Input,
|
||||
} from "sveltestrap";
|
||||
import Filters from "./filters/Filters.svelte";
|
||||
import { queryStore, gql, getContextClient } from "@urql/svelte";
|
||||
import { scramble, scrambleNames } from "./joblist/JobInfo.svelte";
|
||||
import { UniqueInputFieldNamesRule } from "graphql";
|
||||
|
||||
const {} = init();
|
||||
|
||||
@ -21,9 +29,9 @@
|
||||
"Invalid list type provided!"
|
||||
);
|
||||
|
||||
let filter = []
|
||||
let filter = [];
|
||||
|
||||
$: stats = queryStore({
|
||||
const stats = queryStore({
|
||||
client: getContextClient(),
|
||||
query: gql`
|
||||
query($filter: [JobFilter!]!) {
|
||||
@ -36,7 +44,7 @@
|
||||
}
|
||||
}`,
|
||||
variables: { filter },
|
||||
pause: true
|
||||
pause: true,
|
||||
});
|
||||
|
||||
let filters;
|
||||
@ -92,7 +100,7 @@
|
||||
startTimeQuickSelect={true}
|
||||
menuText="Only {type.toLowerCase()}s with jobs that match the filters will show up"
|
||||
on:update={({ detail }) => {
|
||||
$stats.variables = { filter: detail.filters }
|
||||
filter = detail.filters;
|
||||
stats.resume();
|
||||
}}
|
||||
/>
|
||||
@ -102,7 +110,10 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">
|
||||
{({ USER: "Username", PROJECT: "Project Name" })[type]}
|
||||
<!-- {({ -->
|
||||
<!-- USER: "Username", -->
|
||||
<!-- PROJECT: "Project Name", -->
|
||||
<!-- })[type]} -->
|
||||
<Button
|
||||
color={sorting.field == "id" ? "primary" : "light"}
|
||||
size="sm"
|
||||
|
@ -9,84 +9,125 @@
|
||||
- update(filters?: [JobFilter])
|
||||
-->
|
||||
<script>
|
||||
import { queryStore, gql, getContextClient , mutationStore } from '@urql/svelte'
|
||||
import { getContext } from 'svelte';
|
||||
import { Row, Table, Card, Spinner } from 'sveltestrap'
|
||||
import Pagination from './Pagination.svelte'
|
||||
import JobListRow from './Row.svelte'
|
||||
import { stickyHeader } from '../utils.js'
|
||||
import {
|
||||
queryStore,
|
||||
gql,
|
||||
getContextClient,
|
||||
mutationStore,
|
||||
} from "@urql/svelte";
|
||||
import { getContext } from "svelte";
|
||||
import { Row, Table, Card, Spinner } from "sveltestrap";
|
||||
import Pagination from "./Pagination.svelte";
|
||||
import JobListRow from "./Row.svelte";
|
||||
import { stickyHeader } from "../utils.js";
|
||||
|
||||
const ccconfig = getContext('cc-config'),
|
||||
clusters = getContext('clusters'),
|
||||
initialized = getContext('initialized')
|
||||
const ccconfig = getContext("cc-config"),
|
||||
clusters = getContext("clusters"),
|
||||
initialized = getContext("initialized");
|
||||
|
||||
export let sorting = { field: "startTime", order: "DESC" }
|
||||
export let matchedJobs = 0
|
||||
export let metrics = ccconfig.plot_list_selectedMetrics
|
||||
export let sorting = { field: "startTime", order: "DESC" };
|
||||
export let matchedJobs = 0;
|
||||
export let metrics = ccconfig.plot_list_selectedMetrics;
|
||||
|
||||
let itemsPerPage = ccconfig.plot_list_jobsPerPage
|
||||
let page = 1
|
||||
let paging = { itemsPerPage, page }
|
||||
let filter = []
|
||||
let itemsPerPage = ccconfig.plot_list_jobsPerPage;
|
||||
let page = 1;
|
||||
let paging = { itemsPerPage, page };
|
||||
let filter = [];
|
||||
|
||||
$: jobs = queryStore({
|
||||
const jobs = queryStore({
|
||||
client: getContextClient(),
|
||||
query: gql`
|
||||
query($filter: [JobFilter!]!, $sorting: OrderByInput!, $paging: PageRequest! ){
|
||||
query (
|
||||
$filter: [JobFilter!]!
|
||||
$sorting: OrderByInput!
|
||||
$paging: PageRequest!
|
||||
) {
|
||||
jobs(filter: $filter, order: $sorting, page: $paging) {
|
||||
items {
|
||||
id, jobId, user, project, jobName, cluster, subCluster, startTime,
|
||||
duration, numNodes, numHWThreads, numAcc, walltime, resources { hostname },
|
||||
SMT, exclusive, partition, arrayJobId,
|
||||
monitoringStatus, state,
|
||||
tags { id, type, name }
|
||||
userData { name }
|
||||
id
|
||||
jobId
|
||||
user
|
||||
project
|
||||
jobName
|
||||
cluster
|
||||
subCluster
|
||||
startTime
|
||||
duration
|
||||
numNodes
|
||||
numHWThreads
|
||||
numAcc
|
||||
walltime
|
||||
resources {
|
||||
hostname
|
||||
}
|
||||
SMT
|
||||
exclusive
|
||||
partition
|
||||
arrayJobId
|
||||
monitoringStatus
|
||||
state
|
||||
tags {
|
||||
id
|
||||
type
|
||||
name
|
||||
}
|
||||
userData {
|
||||
name
|
||||
}
|
||||
metaData
|
||||
}
|
||||
count
|
||||
}
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
variables: { paging, sorting, filter },
|
||||
pause: true
|
||||
})
|
||||
pause: true,
|
||||
});
|
||||
|
||||
const updateConfiguration = ({ name, value }) => {
|
||||
result = mutationStore({
|
||||
client: getContextClient(),
|
||||
query: gql`mutation($name: String!, $value: String!) {
|
||||
query: gql`
|
||||
mutation ($name: String!, $value: String!) {
|
||||
updateConfiguration(name: $name, value: $value)
|
||||
}`,
|
||||
variables: {name, value}
|
||||
})
|
||||
}
|
||||
`,
|
||||
variables: { name, value },
|
||||
});
|
||||
};
|
||||
|
||||
// $: $jobs.variables = { ...$jobs.variables, sorting, paging }
|
||||
$: matchedJobs = $jobs.data != null ? $jobs.data.jobs.count : 0
|
||||
$: matchedJobs = $jobs.data != null ? $jobs.data.jobs.count : 0;
|
||||
|
||||
// (Re-)query and optionally set new filters.
|
||||
export function update(filters) {
|
||||
if (filters != null) {
|
||||
let minRunningFor = ccconfig.plot_list_hideShortRunningJobs
|
||||
let minRunningFor = ccconfig.plot_list_hideShortRunningJobs;
|
||||
if (minRunningFor && minRunningFor > 0) {
|
||||
filters.push({ minRunningFor })
|
||||
filters.push({ minRunningFor });
|
||||
}
|
||||
|
||||
$jobs.variables.filter = filters
|
||||
filter = filters;
|
||||
// console.log('filters:', ...filters.map(f => Object.entries(f)).flat(2))
|
||||
}
|
||||
|
||||
page = 1
|
||||
$jobs.variables.paging = paging = { page, itemsPerPage };
|
||||
$jobs.context.pause = false
|
||||
$jobs.reexecute({ requestPolicy: 'network-only' })
|
||||
page = 1;
|
||||
paging = paging = { page, itemsPerPage };
|
||||
jobs.resume();
|
||||
// $jobs.reexecute({ requestPolicy: 'network-only' })
|
||||
}
|
||||
|
||||
let tableWidth = null
|
||||
let jobInfoColumnWidth = 250
|
||||
$: plotWidth = Math.floor((tableWidth - jobInfoColumnWidth) / metrics.length - 10)
|
||||
let tableWidth = null;
|
||||
let jobInfoColumnWidth = 250;
|
||||
$: plotWidth = Math.floor(
|
||||
(tableWidth - jobInfoColumnWidth) / metrics.length - 10
|
||||
);
|
||||
|
||||
let headerPaddingTop = 0
|
||||
stickyHeader('.cc-table-wrapper > table.table >thead > tr > th.position-sticky:nth-child(1)', (x) => (headerPaddingTop = x))
|
||||
let headerPaddingTop = 0;
|
||||
stickyHeader(
|
||||
".cc-table-wrapper > table.table >thead > tr > th.position-sticky:nth-child(1)",
|
||||
(x) => (headerPaddingTop = x)
|
||||
);
|
||||
</script>
|
||||
|
||||
<Row>
|
||||
@ -94,20 +135,43 @@
|
||||
<Table cellspacing="0px" cellpadding="0px">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="position-sticky top-0" scope="col" style="width: {jobInfoColumnWidth}px; padding-top: {headerPaddingTop}px">
|
||||
<th
|
||||
class="position-sticky top-0"
|
||||
scope="col"
|
||||
style="width: {jobInfoColumnWidth}px; padding-top: {headerPaddingTop}px"
|
||||
>
|
||||
Job Info
|
||||
</th>
|
||||
{#each metrics as metric (metric)}
|
||||
<th class="position-sticky top-0 text-center" scope="col" style="width: {plotWidth}px; padding-top: {headerPaddingTop}px">
|
||||
<th
|
||||
class="position-sticky top-0 text-center"
|
||||
scope="col"
|
||||
style="width: {plotWidth}px; padding-top: {headerPaddingTop}px"
|
||||
>
|
||||
{metric}
|
||||
{#if $initialized}
|
||||
({clusters
|
||||
.map(cluster => cluster.metricConfig.find(m => m.name == metric))
|
||||
.filter(m => m != null)
|
||||
.map(m => (m.unit?.prefix?m.unit?.prefix:'') + (m.unit?.base?m.unit?.base:'')) // Build unitStr
|
||||
.reduce((arr, unitStr) => arr.includes(unitStr) ? arr : [...arr, unitStr], []) // w/o this, output would be [unitStr, unitStr]
|
||||
.join(', ')
|
||||
})
|
||||
.map((cluster) =>
|
||||
cluster.metricConfig.find(
|
||||
(m) => m.name == metric
|
||||
)
|
||||
)
|
||||
.filter((m) => m != null)
|
||||
.map(
|
||||
(m) =>
|
||||
(m.unit?.prefix
|
||||
? m.unit?.prefix
|
||||
: "") +
|
||||
(m.unit?.base ? m.unit?.base : "")
|
||||
) // Build unitStr
|
||||
.reduce(
|
||||
(arr, unitStr) =>
|
||||
arr.includes(unitStr)
|
||||
? arr
|
||||
: [...arr, unitStr],
|
||||
[]
|
||||
) // w/o this, output would be [unitStr, unitStr]
|
||||
.join(", ")})
|
||||
{/if}
|
||||
</th>
|
||||
{/each}
|
||||
@ -116,25 +180,24 @@
|
||||
<tbody>
|
||||
{#if $jobs.error}
|
||||
<tr>
|
||||
<td colspan="{metrics.length + 1}">
|
||||
<Card body color="danger" class="mb-3"><h2>{$jobs.error.message}</h2></Card>
|
||||
<td colspan={metrics.length + 1}>
|
||||
<Card body color="danger" class="mb-3"
|
||||
><h2>{$jobs.error.message}</h2></Card
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
{:else if $jobs.fetching || !$jobs.data}
|
||||
<tr>
|
||||
<td colspan="{metrics.length + 1}">
|
||||
<td colspan={metrics.length + 1}>
|
||||
<Spinner secondary />
|
||||
</td>
|
||||
</tr>
|
||||
{:else if $jobs.data && $initialized}
|
||||
{#each $jobs.data.jobs.items as job (job)}
|
||||
<JobListRow
|
||||
job={job}
|
||||
metrics={metrics}
|
||||
plotWidth={plotWidth} />
|
||||
<JobListRow {job} {metrics} {plotWidth} />
|
||||
{:else}
|
||||
<tr>
|
||||
<td colspan="{metrics.length + 1}">
|
||||
<td colspan={metrics.length + 1}>
|
||||
No jobs found
|
||||
</td>
|
||||
</tr>
|
||||
@ -146,24 +209,24 @@
|
||||
</Row>
|
||||
|
||||
<Pagination
|
||||
bind:page={page}
|
||||
bind:page
|
||||
{itemsPerPage}
|
||||
itemText="Jobs"
|
||||
totalItems={matchedJobs}
|
||||
on:update={({ detail }) => {
|
||||
if (detail.itemsPerPage != itemsPerPage) {
|
||||
itemsPerPage = detail.itemsPerPage
|
||||
itemsPerPage = detail.itemsPerPage;
|
||||
updateConfiguration({
|
||||
name: "plot_list_jobsPerPage",
|
||||
value: itemsPerPage.toString()
|
||||
}).then(res => {
|
||||
if (res.error)
|
||||
console.error(res.error);
|
||||
})
|
||||
value: itemsPerPage.toString(),
|
||||
}).then((res) => {
|
||||
if (res.error) console.error(res.error);
|
||||
});
|
||||
}
|
||||
|
||||
paging = { itemsPerPage: detail.itemsPerPage, page: detail.page }
|
||||
}} />
|
||||
paging = { itemsPerPage: detail.itemsPerPage, page: detail.page };
|
||||
}}
|
||||
/>
|
||||
|
||||
<style>
|
||||
.cc-table-wrapper {
|
||||
|
@ -9,23 +9,24 @@
|
||||
-->
|
||||
|
||||
<script>
|
||||
import { queryStore, gql, getContextClient } from '@urql/svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import { Card, Spinner } from 'sveltestrap'
|
||||
import MetricPlot from '../plots/MetricPlot.svelte'
|
||||
import JobInfo from './JobInfo.svelte'
|
||||
import { maxScope } from '../utils.js'
|
||||
import { queryStore, gql, getContextClient } from "@urql/svelte";
|
||||
import { getContext } from "svelte";
|
||||
import { Card, Spinner } from "sveltestrap";
|
||||
import MetricPlot from "../plots/MetricPlot.svelte";
|
||||
import JobInfo from "./JobInfo.svelte";
|
||||
import { maxScope } from "../utils.js";
|
||||
|
||||
export let job
|
||||
export let metrics
|
||||
export let plotWidth
|
||||
export let plotHeight = 275
|
||||
export let job;
|
||||
export let metrics;
|
||||
export let plotWidth;
|
||||
export let plotHeight = 275;
|
||||
|
||||
let scopes = [job.numNodes == 1 ? 'core' : 'node']
|
||||
let { id } = job;
|
||||
let scopes = [job.numNodes == 1 ? "core" : "node"];
|
||||
|
||||
const cluster = getContext('clusters').find(c => c.name == job.cluster)
|
||||
const cluster = getContext("clusters").find((c) => c.name == job.cluster);
|
||||
// Get all MetricConfs which include subCluster-specific settings for this job
|
||||
const metricConfig = getContext('metrics')
|
||||
const metricConfig = getContext("metrics");
|
||||
const metricsQuery = queryStore({
|
||||
client: getContextClient(),
|
||||
query: gql`
|
||||
@ -34,88 +35,121 @@
|
||||
name
|
||||
scope
|
||||
metric {
|
||||
unit { prefix, base }, timestep
|
||||
statisticsSeries { min, mean, max }
|
||||
unit {
|
||||
prefix
|
||||
base
|
||||
}
|
||||
timestep
|
||||
statisticsSeries {
|
||||
min
|
||||
mean
|
||||
max
|
||||
}
|
||||
series {
|
||||
hostname, id, data
|
||||
statistics { min, avg, max }
|
||||
hostname
|
||||
id
|
||||
data
|
||||
statistics {
|
||||
min
|
||||
avg
|
||||
max
|
||||
}
|
||||
}
|
||||
}
|
||||
}`,
|
||||
}
|
||||
}
|
||||
`,
|
||||
pause: true,
|
||||
variables: {
|
||||
id: job.id,
|
||||
id,
|
||||
metrics,
|
||||
scopes}
|
||||
})
|
||||
scopes,
|
||||
},
|
||||
});
|
||||
|
||||
const selectScope = (jobMetrics) => jobMetrics.reduce(
|
||||
(a, b) => maxScope([a.scope, b.scope]) == a.scope
|
||||
? (job.numNodes > 1 ? a : b)
|
||||
: (job.numNodes > 1 ? b : a), jobMetrics[0])
|
||||
const selectScope = (jobMetrics) =>
|
||||
jobMetrics.reduce(
|
||||
(a, b) =>
|
||||
maxScope([a.scope, b.scope]) == a.scope
|
||||
? job.numNodes > 1
|
||||
? a
|
||||
: b
|
||||
: job.numNodes > 1
|
||||
? b
|
||||
: a,
|
||||
jobMetrics[0]
|
||||
);
|
||||
|
||||
const sortAndSelectScope = (jobMetrics) => metrics
|
||||
const sortAndSelectScope = (jobMetrics) =>
|
||||
metrics
|
||||
.map(function (name) {
|
||||
// Get MetricConf for this selected/requested metric
|
||||
let thisConfig = metricConfig(cluster, name)
|
||||
let thisSCIndex = thisConfig.subClusters.findIndex(sc => sc.name == job.subCluster)
|
||||
let thisConfig = metricConfig(cluster, name);
|
||||
let thisSCIndex = thisConfig.subClusters.findIndex(
|
||||
(sc) => sc.name == job.subCluster
|
||||
);
|
||||
// Check if Subcluster has MetricConf: If not found (index == -1), no further remove flag check required
|
||||
if (thisSCIndex >= 0) {
|
||||
// SubCluster Config present: Check if remove flag is set
|
||||
if (thisConfig.subClusters[thisSCIndex].remove == true) {
|
||||
// Return null data and informational flag
|
||||
return {removed: true, data: null}
|
||||
return { removed: true, data: null };
|
||||
} else {
|
||||
// load and return metric, if data available
|
||||
let thisMetric = jobMetrics.filter(jobMetric => jobMetric.name == name) // Returns Array
|
||||
let thisMetric = jobMetrics.filter(
|
||||
(jobMetric) => jobMetric.name == name
|
||||
); // Returns Array
|
||||
if (thisMetric.length > 0) {
|
||||
return {removed: false, data: thisMetric}
|
||||
return { removed: false, data: thisMetric };
|
||||
} else {
|
||||
return {removed: false, data: null}
|
||||
return { removed: false, data: null };
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No specific subCluster config: 'remove' flag not set, deemed false -> load and return metric, if data available
|
||||
let thisMetric = jobMetrics.filter(jobMetric => jobMetric.name == name) // Returns Array
|
||||
let thisMetric = jobMetrics.filter(
|
||||
(jobMetric) => jobMetric.name == name
|
||||
); // Returns Array
|
||||
if (thisMetric.length > 0) {
|
||||
return {removed: false, data: thisMetric}
|
||||
return { removed: false, data: thisMetric };
|
||||
} else {
|
||||
return {removed: false, data: null}
|
||||
return { removed: false, data: null };
|
||||
}
|
||||
}
|
||||
})
|
||||
.map(function (jobMetrics) {
|
||||
if (jobMetrics.data != null && jobMetrics.data.length > 0) {
|
||||
return {removed: jobMetrics.removed, data: selectScope(jobMetrics.data)}
|
||||
return {
|
||||
removed: jobMetrics.removed,
|
||||
data: selectScope(jobMetrics.data),
|
||||
};
|
||||
} else {
|
||||
return jobMetrics
|
||||
return jobMetrics;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
$: metricsQuery.variables = { id: job.id, metrics, scopes }
|
||||
// $: metricsQuery.variables = { id: job.id, metrics, scopes };
|
||||
|
||||
if (job.monitoringStatus)
|
||||
$metricsQuery.resume()
|
||||
if (job.monitoringStatus) metricsQuery.resume();
|
||||
</script>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<JobInfo job={job}/>
|
||||
<JobInfo {job} />
|
||||
</td>
|
||||
{#if job.monitoringStatus == 0 || job.monitoringStatus == 2}
|
||||
<td colspan="{metrics.length}">
|
||||
<td colspan={metrics.length}>
|
||||
<Card body color="warning">Not monitored or archiving failed</Card>
|
||||
</td>
|
||||
{:else if $metricsQuery.fetching}
|
||||
<td colspan="{metrics.length}" style="text-align: center;">
|
||||
<td colspan={metrics.length} style="text-align: center;">
|
||||
<Spinner secondary />
|
||||
</td>
|
||||
{:else if $metricsQuery.error}
|
||||
<td colspan="{metrics.length}">
|
||||
<td colspan={metrics.length}>
|
||||
<Card body color="danger" class="mb-3">
|
||||
{$metricsQuery.error.message.length > 500
|
||||
? $metricsQuery.error.message.substring(0, 499)+'...'
|
||||
? $metricsQuery.error.message.substring(0, 499) + "..."
|
||||
: $metricsQuery.error.message}
|
||||
</Card>
|
||||
</td>
|
||||
@ -132,10 +166,13 @@
|
||||
series={metric.data.metric.series}
|
||||
statisticsSeries={metric.data.metric.statisticsSeries}
|
||||
metric={metric.data.name}
|
||||
cluster={cluster}
|
||||
subCluster={job.subCluster} />
|
||||
{cluster}
|
||||
subCluster={job.subCluster}
|
||||
/>
|
||||
{:else if metric.removed == true && metric.data == null}
|
||||
<Card body color="info">Metric disabled for subcluster '{ job.subCluster }'</Card>
|
||||
<Card body color="info"
|
||||
>Metric disabled for subcluster '{job.subCluster}'</Card
|
||||
>
|
||||
{:else}
|
||||
<Card body color="warning">Missing Data</Card>
|
||||
{/if}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { expiringCacheExchange } from "./cache-exchange.js";
|
||||
import {
|
||||
CreateClient,
|
||||
Client,
|
||||
setContextClient,
|
||||
fetchExchange,
|
||||
} from "@urql/svelte";
|
||||
@ -22,7 +22,7 @@ export function init(extraInitQuery = "") {
|
||||
? getContext("jwt")
|
||||
: getContext("cc-config")["jwt"];
|
||||
|
||||
const client = CreateClient({
|
||||
const client = new Client({
|
||||
url: `${window.location.origin}/query`,
|
||||
fetchOptions:
|
||||
jwt != null ? { headers: { Authorization: `Bearer ${jwt}` } } : {},
|
||||
|
Loading…
Reference in New Issue
Block a user