Array and Map support for questions

This commit is contained in:
Vincent Fiduccia 2021-06-15 00:41:45 -07:00
parent dedff10ae5
commit fc92149d0b
No known key found for this signature in database
GPG Key ID: 2B29AD6BB2BB2582
10 changed files with 211 additions and 24 deletions

View File

@ -0,0 +1,33 @@
<script>
import ArrayList from '@/components/form/ArrayList';
import Question from './Question';
export default {
components: { ArrayList },
mixins: [Question],
methods: {
update(val) {
this.$emit('input', val);
}
}
};
</script>
<template>
<div class="row">
<div class="col span-6">
<ArrayList
:key="question.variable"
v-model="value[question.variable]"
:title="question.label"
:mode="mode"
:protip="false"
@input="update"
/>
</div>
<div v-if="showDescription" class="col span-6 mt-10">
{{ question.description }}
</div>
</div>
</template>

View File

@ -12,6 +12,7 @@ export default {
<div class="row">
<div class="col span-6">
<Checkbox
:mode="mode"
:label="displayLabel"
:value="value"
@input="$emit('input', $event)"

View File

@ -12,6 +12,7 @@ export default {
<div class="row">
<div class="col span-6">
<LabeledSelect
:mode="mode"
:label="displayLabel"
:options="question.options"
:placeholder="question.description"

View File

@ -0,0 +1,30 @@
<script>
import LabeledInput from '@/components/form/LabeledInput';
import Question from './Question';
// @TODO valid_chars, invalid_chars
export default {
components: { LabeledInput },
mixins: [Question]
};
</script>
<template>
<div class="row">
<div class="col span-6">
<LabeledInput
type="text"
:mode="mode"
:label="displayLabel"
:placeholder="question.default"
:required="question.required"
:value="value"
@input="val = parseFloat($event); if ( !isNaN(val) ) { $emit('input', val) }"
/>
</div>
<div v-if="showDescription" class="col span-6 mt-10">
{{ question.description }}
</div>
</div>
</template>

View File

@ -15,6 +15,7 @@ export default {
<div class="col span-6">
<LabeledInput
type="text"
:mode="mode"
:label="displayLabel"
:placeholder="question.default"
:required="question.required"

View File

@ -0,0 +1,37 @@
<script>
import KeyValue from '@/components/form/KeyValue';
import Question from './Question';
export default {
components: { KeyValue },
mixins: [Question],
methods: {
update(val) {
this.$emit('input', val);
}
}
};
</script>
<template>
<div>
<div v-if="showDescription" class="row mt-10">
<div class="col span-12">
{{ question.description }}
</div>
</div>
<div class="row">
<div class="col span-12 mt-10">
<KeyValue
:key="question.variable"
v-model="value[question.variable]"
:title="question.label"
:mode="mode"
:protip="false"
@input="update"
/>
</div>
</div>
</div>
</template>

View File

@ -1,3 +1,5 @@
import { _EDIT } from '@/config/query-params';
export default {
props: {
question: {
@ -5,6 +7,11 @@ export default {
required: true,
},
mode: {
type: String,
default: _EDIT,
},
// targetNamespace: {
// type: String,
// required: true,

View File

@ -28,6 +28,7 @@ export default {
<div>
Secret in namespace {{ targetNamespace }}
<LabeledSelect
:mode="mode"
:disabled="$fetchState.pending"
:label="displayLabel"
:placeholder="question.description"

View File

@ -24,6 +24,7 @@ export default {
<div class="row">
<div class="col span-6">
<LabeledInput
:mode="mode"
:type="inputType"
:label="displayLabel"
:placeholder="question.default"

View File

@ -2,12 +2,17 @@
import Jexl from 'jexl';
import Tab from '@/components/Tabbed/Tab';
import { get, set } from '@/utils/object';
import sortBy from 'lodash/sortBy';
import { _EDIT } from '@/config/query-params';
import StringType from './String';
import BooleanType from './Boolean';
import EnumType from './Enum';
import IntType from './Int';
import FloatType from './Float';
import ArrayType from './Array';
import MapType from './Map';
const knownTypes = {
export const knownTypes = {
string: StringType,
hostname: StringType, // @TODO
multiline: StringType,
@ -15,11 +20,40 @@ const knownTypes = {
boolean: BooleanType,
enum: EnumType,
int: IntType,
float: FloatType,
map: MapType,
// storageclass
// pvc
// secret
};
export function componentForQuestion(q) {
if ( knownTypes[q.type] ) {
return q.type;
} else if ( q.type.startsWith('array[') ) { // This only really works for array[string|multiline], but close enough for now.
return ArrayType;
} else if ( q.type.startsWith('map[') ) { // Same, only works with map[string|multiline]
return MapType;
}
return 'string';
}
export function schemaToQuestions(schema) {
const keys = Object.keys(schema.resourceFields);
const out = [];
for ( const k of keys ) {
out.push({
variable: k,
label: k,
...schema.resourceFields[k],
});
}
return out;
}
function evalExpr(expr, values) {
try {
const out = Jexl.evalSync(expr, values);
@ -120,12 +154,23 @@ export default {
components: { Tab, ...knownTypes },
props: {
mode: {
type: String,
default: _EDIT,
},
value: {
type: Object,
required: true,
},
chartVersion: {
tabbed: {
type: Boolean,
default: true,
},
// Can be a chartVersion or a resource Schema
source: {
type: Object,
required: true,
},
@ -133,7 +178,12 @@ export default {
targetNamespace: {
type: String,
required: true
}
},
ignoreVariables: {
type: Array,
default: () => [],
},
},
data() {
@ -142,7 +192,13 @@ export default {
computed: {
allQuestions() {
return this.chartVersion.questions.questions;
if ( this.source.type === 'schema' ) {
return schemaToQuestions(this.source);
} else if ( this.source.questions?.questions ) {
return this.chartVersion.questions.questions;
} else {
throw new Error('Must specify sourec as a chartVersion or Schema resource');
}
},
shownQuestions() {
@ -156,6 +212,10 @@ export default {
const out = [];
for ( const q of this.allQuestions ) {
if ( this.ignoreVariables.includes(q.variable) ) {
continue;
}
addQuestion(q);
}
@ -185,24 +245,24 @@ export default {
let weight = this.shownQuestions.length;
for ( const q of this.shownQuestions ) {
if ( q.group ) {
const normalized = q.group.trim().toLowerCase();
const group = q.group || defaultGroup;
if ( !map[normalized] ) {
map[normalized] = {
name: q.group || defaultGroup,
questions: [],
weight: weight--,
};
}
const normalized = group.trim().toLowerCase();
map[normalized].questions.push(q);
if ( !map[normalized] ) {
map[normalized] = {
name: group,
questions: [],
weight: weight--,
};
}
map[normalized].questions.push(q);
}
const out = Object.values(map);
return out;
return sortBy(out, 'weight:desc');
},
},
@ -219,20 +279,13 @@ export default {
methods: {
get,
set,
componentForQuestion(q) {
if ( knownTypes[q.type] ) {
return q.type;
}
return 'string';
}
componentForQuestion,
},
};
</script>
<template>
<div>
<div v-if="tabbed">
<Tab
v-for="g in groups"
:key="g.name"
@ -253,6 +306,28 @@ export default {
</div>
</Tab>
</div>
<div v-else>
<div
v-for="g in groups"
:key="g.name"
>
<h3 v-if="groups.length > 1">
{{ g.label }}
</h3>
<div v-for="q in g.questions" :key="q.variable" class="row question">
<div class="col span-12">
<component
:is="componentForQuestion(q)"
:question="q"
:target-namespace="targetNamespace"
:mode="mode"
:value="get(value, q.variable)"
@input="set(value, q.variable, $event)"
/>
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>