Much more strictly define types and use type guards
This commit is contained in:
parent
e684dfcd04
commit
ae62484d30
91
src/App.vue
91
src/App.vue
@ -15,7 +15,7 @@
|
|||||||
v-for="(event, index) in minion_events"
|
v-for="(event, index) in minion_events"
|
||||||
:key="index"
|
:key="index"
|
||||||
>
|
>
|
||||||
<summary>{{ event.tag }}</summary>
|
<summary>{{ event_name(event) }}</summary>
|
||||||
<pre>{{ JSON.stringify(event, null, 2) }}</pre>
|
<pre>{{ JSON.stringify(event, null, 2) }}</pre>
|
||||||
</details>
|
</details>
|
||||||
</details>
|
</details>
|
||||||
@ -29,14 +29,33 @@ import { Vue, Component, Prop, Ref } from 'vue-property-decorator';
|
|||||||
|
|
||||||
import credentials from './credentials.json';
|
import credentials from './credentials.json';
|
||||||
|
|
||||||
|
import * as salt from './salt';
|
||||||
|
|
||||||
|
function isEventType<T extends salt.SaltEvent['splitTag'][1]>(
|
||||||
|
event: salt.SaltEvent,
|
||||||
|
type: T
|
||||||
|
): event is Extract<salt.SaltEvent, { splitTag: ['salt', T, ...any[]] }> {
|
||||||
|
return event.splitTag[1] === type;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isJobEventType<T extends salt.SaltEvent['splitTag'][3]>(
|
||||||
|
event: salt.SaltEvent,
|
||||||
|
type: T
|
||||||
|
): event is Extract<
|
||||||
|
salt.SaltEvent,
|
||||||
|
{ splitTag: ['salt', 'job', any, T, ...any[]] }
|
||||||
|
> {
|
||||||
|
return event.splitTag[3] === type;
|
||||||
|
}
|
||||||
|
|
||||||
const BASE_URL = 'https://salt.sawtooth.claremontmakerspace.org:8000/';
|
const BASE_URL = 'https://salt.sawtooth.claremontmakerspace.org:8000/';
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
export default class App extends Vue {
|
export default class App extends Vue {
|
||||||
evtSource: EventSource | null = null;
|
evtSource: EventSource | null = null;
|
||||||
events: any[] = [];
|
events: salt.SaltEvent[] = [];
|
||||||
|
|
||||||
mounted() {
|
mounted(): void {
|
||||||
fetch(BASE_URL + 'login', {
|
fetch(BASE_URL + 'login', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
@ -44,50 +63,58 @@ export default class App extends Vue {
|
|||||||
})
|
})
|
||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(r => {
|
.then(r => {
|
||||||
const token = r.return[0].token;
|
const token: string = r.return[0].token;
|
||||||
this.evtSource = new EventSource(BASE_URL + 'events?token=' + token);
|
this.evtSource = new EventSource(BASE_URL + 'events?token=' + token);
|
||||||
this.evtSource.onmessage = e => {
|
this.evtSource.onmessage = e => {
|
||||||
this.events.push(JSON.parse(e.data));
|
const evt = JSON.parse(e.data);
|
||||||
|
evt.splitTag = evt.tag.split('/');
|
||||||
|
this.events.push(evt);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_tag(tag) {
|
event_name(event: salt.JobEvent) {
|
||||||
// todo: would probably be easier as string splitting at this point
|
if (isJobEventType(event, 'prog')) {
|
||||||
const match = tag.match(/salt\/job\/([^\/]*)\/(prog|ret)\/([^\/]*)\/?(.*)/);
|
return `${this.event_symbol(event)} Progress: ${
|
||||||
if (!match) return null;
|
event.data.data.ret.name
|
||||||
return {
|
}|${event.data.data.ret.duration} `;
|
||||||
jid: match[1],
|
} else if (isJobEventType(event, 'ret')) {
|
||||||
type: match[2],
|
return `${this.event_symbol(event)} Return: ${event.data.fun} `;
|
||||||
minion: match[3],
|
}
|
||||||
prog_id: match.length >= 5 ? match[4] : null,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
event_symbol(event) {
|
event_symbol(event: salt.JobEvent) {
|
||||||
const tag = this.parse_tag(event.tag);
|
if (isJobEventType(event, 'prog')) {
|
||||||
if (tag.type === 'prog') {
|
|
||||||
const ret = event.data.data.ret;
|
const ret = event.data.data.ret;
|
||||||
if (!ret.result) return '✗';
|
if (!ret.result) return '✗';
|
||||||
else if ('ret' in ret.changes) return 'Δ';
|
else if (Object.keys(ret.changes).length !== 0) return 'Δ';
|
||||||
else return '✓';
|
else return '✓';
|
||||||
} else if (tag.type === 'ret') {
|
} else if (isJobEventType(event, 'ret')) {
|
||||||
return event.data.success ? '✓' : '✗';
|
return event.data.success ? '✓' : '✗';
|
||||||
}
|
}
|
||||||
|
|
||||||
return '?';
|
return '?';
|
||||||
}
|
}
|
||||||
|
|
||||||
get jobs() {
|
// {jid: {mid: event[]}}
|
||||||
return this.events.reduce((acc, e) => {
|
get jobs(): { [key: string]: { [key: string]: salt.JobEvent[] } } {
|
||||||
const tag = this.parse_tag(e.tag);
|
return this.events.reduce(
|
||||||
if (tag) {
|
(
|
||||||
if (!(tag.jid in acc)) acc[tag.jid] = {};
|
acc: { [key: string]: { [key: string]: salt.JobEvent[] } },
|
||||||
if (!(tag.minion in acc[tag.jid])) acc[tag.jid][tag.minion] = [];
|
e: salt.SaltEvent
|
||||||
acc[tag.jid][tag.minion].push(e);
|
) => {
|
||||||
}
|
if (
|
||||||
return acc;
|
isEventType(e, 'job') &&
|
||||||
}, {});
|
(isJobEventType(e, 'prog') || isJobEventType(e, 'ret'))
|
||||||
|
) {
|
||||||
|
if (!(e.data.jid in acc)) acc[e.data.jid] = {};
|
||||||
|
if (!(e.data.id in acc[e.data.jid])) acc[e.data.jid][e.data.id] = [];
|
||||||
|
acc[e.data.jid][e.data.id].push(e);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -96,4 +123,8 @@ export default class App extends Vue {
|
|||||||
.minion .event {
|
.minion .event {
|
||||||
margin-left: 2ex;
|
margin-left: 2ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
95
src/salt.d.ts
vendored
Normal file
95
src/salt.d.ts
vendored
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
export type MinionID = string;
|
||||||
|
export type JobID = string;
|
||||||
|
|
||||||
|
export type SaltEvent = AuthenticationEvent | StartEvent | KeyEvent | JobEvent;
|
||||||
|
// | RunnerEvent
|
||||||
|
// | PresenceEvent
|
||||||
|
// | CloudEvent;
|
||||||
|
|
||||||
|
export interface AuthenticationEvent {
|
||||||
|
tag: 'salt/auth';
|
||||||
|
splitTag: ['salt', 'auth'];
|
||||||
|
data: {
|
||||||
|
id: MinionID;
|
||||||
|
act: 'accept' | 'pend' | 'reject';
|
||||||
|
pub: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StartEvent {
|
||||||
|
tag: string; // salt/minion/<MID>/start
|
||||||
|
splitTag: ['salt', 'minion', string, 'start'];
|
||||||
|
data: {
|
||||||
|
id: MinionID;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface KeyEvent {
|
||||||
|
tag: 'salt/key';
|
||||||
|
splitTag: ['salt', 'key'];
|
||||||
|
data: {
|
||||||
|
id: MinionID;
|
||||||
|
act: 'accept' | 'delete';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type JobEvent = JobNewEvent | JobRetEvent | JobProgEvent;
|
||||||
|
|
||||||
|
export interface JobNewEvent {
|
||||||
|
tag: string; // salt/job/<JID>/new
|
||||||
|
splitTag: ['salt', 'job', string, 'new'];
|
||||||
|
data: {
|
||||||
|
jid: JobID;
|
||||||
|
tgt: string;
|
||||||
|
tgt_type: 'glob' | 'grain' | 'compound' | string;
|
||||||
|
fun: string;
|
||||||
|
arg: [string];
|
||||||
|
minions: [MinionID];
|
||||||
|
user: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface JobRetEvent {
|
||||||
|
tag: string; // salt/job/<JID>/ret/<MID>
|
||||||
|
splitTag: ['salt', 'job', string, 'ret', string];
|
||||||
|
data: {
|
||||||
|
id: MinionID;
|
||||||
|
jid: JobID;
|
||||||
|
retcode: number;
|
||||||
|
fun: string;
|
||||||
|
fun_args: [string];
|
||||||
|
success: boolean;
|
||||||
|
return: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface JobProgEvent {
|
||||||
|
tag: string; // salt/job/<JID>/prog/<MID>/<RUN NUM>
|
||||||
|
splitTag: ['salt', 'job', string, 'prog', string, string];
|
||||||
|
data: {
|
||||||
|
data: any;
|
||||||
|
id: MinionID;
|
||||||
|
jid: JobID;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
export interface RunnerEvent {
|
||||||
|
tag: string;
|
||||||
|
splitTag: [];
|
||||||
|
data: {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
export interface PresenceEvent {
|
||||||
|
tag: string;
|
||||||
|
splitTag: [];
|
||||||
|
data: {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
export interface CloudEvent {
|
||||||
|
tag: string;
|
||||||
|
splitTag: [];
|
||||||
|
data: {};
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user