mirror of
https://github.com/Luzifer/twitch-bot.git
synced 2024-11-09 16:50:01 +00:00
[editor] Add validation for template fields
closes #38 Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
0dc648d02a
commit
4f12b5c206
5 changed files with 100 additions and 6 deletions
|
@ -172,6 +172,10 @@ func patchConfig(filename, authorName, authorEmail, summary string, patcher func
|
||||||
return errors.Wrap(err, "patching config")
|
return errors.Wrap(err, "patching config")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = cfgFile.runLoadChecks(); err != nil {
|
||||||
|
return errors.Wrap(err, "checking config after patch")
|
||||||
|
}
|
||||||
|
|
||||||
return errors.Wrap(
|
return errors.Wrap(
|
||||||
writeConfigToYAML(filename, authorName, authorEmail, summary, cfgFile),
|
writeConfigToYAML(filename, authorName, authorEmail, summary, cfgFile),
|
||||||
"replacing config",
|
"replacing config",
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
|
|
||||||
var frontendReloadHooks = newHooker()
|
var frontendReloadHooks = newHooker()
|
||||||
|
|
||||||
|
//nolint:funlen // Just contains a collection of objects
|
||||||
func registerEditorGlobalMethods() {
|
func registerEditorGlobalMethods() {
|
||||||
for _, rd := range []plugins.HTTPRouteRegistrationArgs{
|
for _, rd := range []plugins.HTTPRouteRegistrationArgs{
|
||||||
{
|
{
|
||||||
|
@ -100,6 +101,23 @@ func registerEditorGlobalMethods() {
|
||||||
},
|
},
|
||||||
ResponseType: plugins.HTTPRouteResponseTypeTextPlain,
|
ResponseType: plugins.HTTPRouteResponseTypeTextPlain,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Description: "Validate a template expression against the built in template function library",
|
||||||
|
HandlerFunc: configEditorGlobalValidateTemplate,
|
||||||
|
Method: http.MethodPut,
|
||||||
|
Module: "config-editor",
|
||||||
|
Name: "Validate template expression",
|
||||||
|
Path: "/validate-template",
|
||||||
|
QueryParams: []plugins.HTTPRouteParamDocumentation{
|
||||||
|
{
|
||||||
|
Description: "The template expression to test",
|
||||||
|
Name: "template",
|
||||||
|
Required: true,
|
||||||
|
Type: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ResponseType: plugins.HTTPRouteResponseTypeTextPlain,
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
if err := registerRoute(rd); err != nil {
|
if err := registerRoute(rd); err != nil {
|
||||||
log.WithError(err).Fatal("Unable to register config editor route")
|
log.WithError(err).Fatal("Unable to register config editor route")
|
||||||
|
@ -215,3 +233,12 @@ func configEditorGlobalValidateRegex(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func configEditorGlobalValidateTemplate(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if err := validateTemplate(r.FormValue("template")); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
|
@ -124,6 +124,7 @@
|
||||||
id="formAutoMessageMessage"
|
id="formAutoMessageMessage"
|
||||||
v-model="models.autoMessage.message"
|
v-model="models.autoMessage.message"
|
||||||
:state="models.autoMessage.message ? models.autoMessage.message.length <= validateAutoMessageMessageLength : false"
|
:state="models.autoMessage.message ? models.autoMessage.message.length <= validateAutoMessageMessageLength : false"
|
||||||
|
@valid-template="valid => updateTemplateValid('autoMessage.message', valid)"
|
||||||
/>
|
/>
|
||||||
<div slot="description">
|
<div slot="description">
|
||||||
<font-awesome-icon
|
<font-awesome-icon
|
||||||
|
@ -227,6 +228,7 @@
|
||||||
<template-editor
|
<template-editor
|
||||||
id="formAutoMessageDisableOnTemplate"
|
id="formAutoMessageDisableOnTemplate"
|
||||||
v-model="models.autoMessage.disable_on_template"
|
v-model="models.autoMessage.disable_on_template"
|
||||||
|
@valid-template="valid => updateTemplateValid('autoMessage.disable_on_template', valid)"
|
||||||
/>
|
/>
|
||||||
</b-form-group>
|
</b-form-group>
|
||||||
</b-col>
|
</b-col>
|
||||||
|
@ -284,6 +286,10 @@ export default {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Object.entries(this.templateValid).filter(e => !e[1]).length > 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -344,6 +350,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
showAutoMessageEditModal: false,
|
showAutoMessageEditModal: false,
|
||||||
|
templateValid: {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -376,6 +383,7 @@ export default {
|
||||||
...msg,
|
...msg,
|
||||||
sendMode: msg.cron ? 'cron' : 'lines',
|
sendMode: msg.cron ? 'cron' : 'lines',
|
||||||
})
|
})
|
||||||
|
this.templateValid = {}
|
||||||
this.showAutoMessageEditModal = true
|
this.showAutoMessageEditModal = true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -392,6 +400,7 @@ export default {
|
||||||
|
|
||||||
newAutoMessage() {
|
newAutoMessage() {
|
||||||
Vue.set(this.models, 'autoMessage', {})
|
Vue.set(this.models, 'autoMessage', {})
|
||||||
|
this.templateValid = {}
|
||||||
this.showAutoMessageEditModal = true
|
this.showAutoMessageEditModal = true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -422,6 +431,10 @@ export default {
|
||||||
})
|
})
|
||||||
.catch(err => this.$bus.$emit(constants.NOTIFY_FETCH_ERROR, err))
|
.catch(err => this.$bus.$emit(constants.NOTIFY_FETCH_ERROR, err))
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateTemplateValid(id, valid) {
|
||||||
|
Vue.set(this.templateValid, id, valid)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
|
@ -398,6 +398,7 @@
|
||||||
<template-editor
|
<template-editor
|
||||||
id="formRuleDisableOnTemplate"
|
id="formRuleDisableOnTemplate"
|
||||||
v-model="models.rule.disable_on_template"
|
v-model="models.rule.disable_on_template"
|
||||||
|
@valid-template="valid => updateTemplateValid('rule.disable_on_template', valid)"
|
||||||
/>
|
/>
|
||||||
</b-form-group>
|
</b-form-group>
|
||||||
</b-tab>
|
</b-tab>
|
||||||
|
@ -603,6 +604,7 @@
|
||||||
:id="`${models.rule.uuid}-action-${idx}-${field.key}`"
|
:id="`${models.rule.uuid}-action-${idx}-${field.key}`"
|
||||||
v-model="models.rule.actions[idx].attributes[field.key]"
|
v-model="models.rule.actions[idx].attributes[field.key]"
|
||||||
:state="validateActionArgument(idx, field.key)"
|
:state="validateActionArgument(idx, field.key)"
|
||||||
|
@valid-template="valid => updateTemplateValid(`${models.rule.uuid}-action-${idx}-${field.key}`, valid)"
|
||||||
/>
|
/>
|
||||||
</b-form-group>
|
</b-form-group>
|
||||||
|
|
||||||
|
@ -781,6 +783,7 @@ export default {
|
||||||
|
|
||||||
showRuleEditModal: false,
|
showRuleEditModal: false,
|
||||||
showRuleSubscribeModal: false,
|
showRuleSubscribeModal: false,
|
||||||
|
templateValid: {},
|
||||||
validateReason: null,
|
validateReason: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -850,6 +853,7 @@ export default {
|
||||||
cooldown: this.fixDurationRepresentationToString(msg.cooldown),
|
cooldown: this.fixDurationRepresentationToString(msg.cooldown),
|
||||||
user_cooldown: this.fixDurationRepresentationToString(msg.user_cooldown),
|
user_cooldown: this.fixDurationRepresentationToString(msg.user_cooldown),
|
||||||
})
|
})
|
||||||
|
this.templateValid = {}
|
||||||
this.showRuleEditModal = true
|
this.showRuleEditModal = true
|
||||||
this.validateMatcherRegex()
|
this.validateMatcherRegex()
|
||||||
},
|
},
|
||||||
|
@ -976,6 +980,7 @@ export default {
|
||||||
|
|
||||||
newRule() {
|
newRule() {
|
||||||
Vue.set(this.models, 'rule', { match_message__validation: true })
|
Vue.set(this.models, 'rule', { match_message__validation: true })
|
||||||
|
this.templateValid = {}
|
||||||
this.showRuleEditModal = true
|
this.showRuleEditModal = true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1094,6 +1099,10 @@ export default {
|
||||||
.catch(err => this.$bus.$emit(constants.NOTIFY_FETCH_ERROR, err))
|
.catch(err => this.$bus.$emit(constants.NOTIFY_FETCH_ERROR, err))
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateTemplateValid(id, valid) {
|
||||||
|
Vue.set(this.templateValid, id, valid)
|
||||||
|
},
|
||||||
|
|
||||||
validateActionArgument(idx, key) {
|
validateActionArgument(idx, key) {
|
||||||
const action = this.models.rule.actions[idx]
|
const action = this.models.rule.actions[idx]
|
||||||
const def = this.getActionDefinitionByType(action.type)
|
const def = this.getActionDefinitionByType(action.type)
|
||||||
|
@ -1189,6 +1198,11 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
validateRule() {
|
validateRule() {
|
||||||
|
if (Object.entries(this.templateValid).filter(e => !e[1]).length > 0) {
|
||||||
|
this.validateReason = 'templateValid'
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.models.rule.match_message__validation) {
|
if (!this.models.rule.match_message__validation) {
|
||||||
this.validateReason = 'rule.match_message__validation'
|
this.validateReason = 'rule.match_message__validation'
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -1,11 +1,20 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="wrapClasses">
|
<div>
|
||||||
<div ref="editor" />
|
<div :class="wrapClasses">
|
||||||
|
<div ref="editor" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="!isValid && validationError"
|
||||||
|
class="d-block invalid-feedback"
|
||||||
|
>
|
||||||
|
{{ validationError }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import * as constants from './const.js'
|
import * as constants from './const.js'
|
||||||
|
import axios from 'axios'
|
||||||
import { CodeJar } from 'codejar/codejar.js'
|
import { CodeJar } from 'codejar/codejar.js'
|
||||||
import Prism from 'prismjs'
|
import Prism from 'prismjs'
|
||||||
import { withLineNumbers } from 'codejar/linenumbers.js'
|
import { withLineNumbers } from 'codejar/linenumbers.js'
|
||||||
|
@ -38,8 +47,8 @@ export default {
|
||||||
wrapClasses() {
|
wrapClasses() {
|
||||||
return {
|
return {
|
||||||
'form-control': true,
|
'form-control': true,
|
||||||
'is-invalid': this.state === false,
|
'is-invalid': this.state === false || !this.isValid,
|
||||||
'is-valid': this.state === true,
|
'is-valid': this.state === true && this.isValid,
|
||||||
'template-editor': true,
|
'template-editor': true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -48,7 +57,9 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
emittedCode: '',
|
emittedCode: '',
|
||||||
|
isValid: true,
|
||||||
jar: null,
|
jar: null,
|
||||||
|
validationError: '',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -57,6 +68,27 @@ export default {
|
||||||
const code = editor.textContent
|
const code = editor.textContent
|
||||||
editor.innerHTML = Prism.highlight(code, this.grammar, 'template')
|
editor.innerHTML = Prism.highlight(code, this.grammar, 'template')
|
||||||
},
|
},
|
||||||
|
|
||||||
|
validateTemplate(template) {
|
||||||
|
if (template === '') {
|
||||||
|
this.isValid = true
|
||||||
|
this.validationError = ''
|
||||||
|
this.$emit('valid-template', true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return axios.put(`config-editor/validate-template?template=${encodeURIComponent(template)}`)
|
||||||
|
.then(() => {
|
||||||
|
this.isValid = true
|
||||||
|
this.validationError = ''
|
||||||
|
this.$emit('valid-template', true)
|
||||||
|
})
|
||||||
|
.catch(resp => {
|
||||||
|
this.isValid = false
|
||||||
|
this.validationError = resp.response.data.split(':1:')[1]
|
||||||
|
this.$emit('valid-template', false)
|
||||||
|
})
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -65,6 +97,7 @@ export default {
|
||||||
tab: ' '.repeat(2),
|
tab: ' '.repeat(2),
|
||||||
})
|
})
|
||||||
this.jar.onUpdate(code => {
|
this.jar.onUpdate(code => {
|
||||||
|
this.validateTemplate(code)
|
||||||
this.emittedCode = code
|
this.emittedCode = code
|
||||||
this.$emit('input', code)
|
this.$emit('input', code)
|
||||||
})
|
})
|
||||||
|
@ -101,8 +134,6 @@ export default {
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.template-editor {
|
.template-editor {
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
color: #444;
|
color: #444;
|
||||||
font-family: SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;
|
font-family: SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;
|
||||||
font-size: 87.5%;
|
font-size: 87.5%;
|
||||||
|
@ -110,6 +141,11 @@ export default {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.template-editor .codejar-wrap {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
.template-editor .codejar-linenumbers {
|
.template-editor .codejar-linenumbers {
|
||||||
padding-right: 0.5em;
|
padding-right: 0.5em;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|
Loading…
Reference in a new issue