[Execute panel] Added cURL snippet modal.
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Fabio Manganiello 2023-10-11 16:38:38 +02:00
parent fc21e9740b
commit b47e729012
Signed by: blacklight
GPG Key ID: D90FBA7F76362774
5 changed files with 133 additions and 60 deletions

View File

@ -1,5 +1,6 @@
<script> <script>
import Api from "@/utils/Api"; import Api from "@/utils/Api";
import Clipboard from "@/utils/Clipboard";
import Cookies from "@/utils/Cookies"; import Cookies from "@/utils/Cookies";
import DateTime from "@/utils/DateTime"; import DateTime from "@/utils/DateTime";
import Events from "@/utils/Events"; import Events from "@/utils/Events";
@ -10,6 +11,16 @@ import Types from "@/utils/Types";
export default { export default {
name: "Utils", name: "Utils",
mixins: [Api, Cookies, Notification, Events, DateTime, Screen, Text, Types], mixins: [
Api,
Clipboard,
Cookies,
DateTime,
Events,
Notification,
Screen,
Text,
Types,
],
} }
</script> </script>

View File

@ -6,6 +6,12 @@
<main> <main>
<h1>Execute Action</h1> <h1>Execute Action</h1>
<!-- cURL snippet modal -->
<Modal ref="curlModal" title="curl request" v-if="curlSnippet?.length">
<textarea class="output curl-snippet" readonly :value="curlSnippet"
@click="copyToClipboard(curlSnippet)" />
</Modal>
<!-- Execute panel views --> <!-- Execute panel views -->
<Tabs> <Tabs>
<Tab :selected="structuredInput" icon-class="fas fa-list" @input="onInputTypeChange(true)"> <Tab :selected="structuredInput" icon-class="fas fa-list" @input="onInputTypeChange(true)">
@ -45,8 +51,16 @@
<!-- Action documentation container --> <!-- Action documentation container -->
<section class="doc-container" v-if="selectedDoc"> <section class="doc-container" v-if="selectedDoc">
<h2> <h2>
<i class="fas fa-book" /> &nbsp; <div class="title">
<a :href="this.action?.doc_url">Action documentation</a> <i class="fas fa-book" /> &nbsp;
<a :href="this.action?.doc_url">Action documentation</a>
</div>
<div class="buttons" v-if="action?.name">
<button type="button" title="cURL command" v-if="curlSnippet?.length"
@click="$refs.curlModal.show()">
<i class="fas fa-terminal" />
</button>
</div>
</h2> </h2>
<div class="doc html"> <div class="doc html">
@ -129,7 +143,7 @@
{{ error != null ? 'Error' : 'Output' }} {{ error != null ? 'Error' : 'Output' }}
</span> </span>
<span class="buttons"> <span class="buttons">
<button type="button" title="Copy to clipboard" @click="copyToClipboard"> <button type="button" title="Copy to clipboard" @click="copyToClipboard(response)">
<i class="fas fa-clipboard" /> <i class="fas fa-clipboard" />
</button> </button>
</span> </span>
@ -159,7 +173,7 @@
<hgroup v-if="error != null || response != null"> <hgroup v-if="error != null || response != null">
<h2 v-text="error != null ? 'Error' : 'Output'" /> <h2 v-text="error != null ? 'Error' : 'Output'" />
<div class="buttons"> <div class="buttons">
<button type="button" title="Copy to clipboard" @click="copyToClipboard"> <button type="button" title="Copy to clipboard" @click="copyToClipboard(error)">
<i class="fas fa-clipboard" /> <i class="fas fa-clipboard" />
</button> </button>
</div> </div>
@ -207,13 +221,14 @@
import Argdoc from "./Argdoc" import Argdoc from "./Argdoc"
import Autocomplete from "@/components/elements/Autocomplete" import Autocomplete from "@/components/elements/Autocomplete"
import Loading from "@/components/Loading" import Loading from "@/components/Loading"
import Modal from "@/components/Modal";
import Tab from "@/components/elements/Tab" import Tab from "@/components/elements/Tab"
import Tabs from "@/components/elements/Tabs" import Tabs from "@/components/elements/Tabs"
import Utils from "@/Utils" import Utils from "@/Utils"
export default { export default {
name: "Execute", name: "Execute",
components: {Argdoc, Autocomplete, Loading, Tab, Tabs}, components: {Argdoc, Autocomplete, Loading, Modal, Tab, Tabs},
mixins: [Utils], mixins: [Utils],
data() { data() {
@ -264,6 +279,64 @@ export default {
actionInput() { actionInput() {
return this.$refs.autocomplete.$el.parentElement.querySelector('input[type=text]') return this.$refs.autocomplete.$el.parentElement.querySelector('input[type=text]')
}, },
requestArgs() {
if (!this.action.name)
return {}
return {
...Object.entries(this.action.args).reduce((args, arg) => {
if (arg[1].value != null) {
let value = arg[1].value
try {
value = JSON.parse(value)
} catch (e) {
console.debug('Not a valid JSON value')
console.debug(value)
}
args[arg[0]] = value
}
return args
}, {}),
...this.action.extraArgs.reduce((args, arg) => {
let value = args[arg.value]
try {
value = JSON.parse(value)
} catch (e) {
console.debug('Not a valid JSON value')
console.debug(value)
}
args[arg.name] = value
return args
}, {})
}
},
curlURL() {
return `${window.location.protocol}//${window.location.host}/execute`
},
curlSnippet() {
if (!this.action.name)
return ''
const request = {
type: 'request',
action: this.action.name,
args: this.requestArgs,
}
return (
'curl -XPOST -H "Content-Type: application/json" \\\n\t' +
`-H "Cookie: session_token=${this.getCookies()['session_token']}"`+
" \\\n\t -d '" +
this.indent(JSON.stringify(request, null, 2), 2).trim() + "' \\\n\t" +
`'${this.curlURL}'`
)
},
}, },
methods: { methods: {
@ -421,14 +494,6 @@ export default {
this.running = false this.running = false
}, },
async copyToClipboard() {
const output = (
this.error != null ? this.error : this.response
)
await navigator.clipboard.writeText(output)
},
getPluginName(actionName) { getPluginName(actionName) {
if (!actionName?.length) if (!actionName?.length)
return '' return ''
@ -442,37 +507,7 @@ export default {
this.running = true this.running = true
if (this.structuredInput) { if (this.structuredInput) {
const args = { this.request(this.action.name, this.requestArgs).then(this.onResponse).catch(this.onError).finally(this.onDone)
...Object.entries(this.action.args).reduce((args, arg) => {
if (arg[1].value != null) {
let value = arg[1].value
try {
value = JSON.parse(value)
} catch (e) {
console.debug('Not a valid JSON value')
console.debug(value)
}
args[arg[0]] = value
}
return args
}, {}),
...this.action.extraArgs.reduce((args, arg) => {
let value = args[arg.value]
try {
value = JSON.parse(value)
} catch (e) {
console.debug('Not a valid JSON value')
console.debug(value)
}
args[arg.name] = value
return args
}, {})
}
this.request(this.action.name, args).then(this.onResponse).catch(this.onError).finally(this.onDone)
} else { } else {
try { try {
const request = JSON.parse(this.rawRequest) const request = JSON.parse(this.rawRequest)

View File

@ -207,7 +207,7 @@ form {
} }
} }
.response { .response, .doc-container {
flex-grow: 1; flex-grow: 1;
h2 { h2 {
@ -225,22 +225,28 @@ form {
} }
} }
} }
}
.output { .output {
background: $output-bg; background: $output-bg;
padding: 0 0.75em; padding: 0 0.75em;
overflow: auto; overflow: auto;
margin-top: 0.1em; margin-top: 0.1em;
border-radius: 1em; border-radius: 1em;
box-shadow: $output-shadow; box-shadow: $output-shadow;
color: $response-fg;
&.response { &.error {
color: $response-fg; color: $error-fg;
}
&.error {
color: $error-fg;
}
} }
} }
textarea.curl-snippet {
width: calc(100vw - 5em);
height: 100vh;
max-width: 40em;
max-height: 25em;
line-break: anywhere;
overflow: auto;
padding: 0.5em;
}

View File

@ -0,0 +1,17 @@
<script>
export default {
name: "Clipboard",
methods: {
async copyToClipboard(text) {
await navigator.clipboard.writeText(text)
this.notify({
text: 'Copied to the clipboard',
image: {
icon: 'clipboard',
},
})
},
},
}
</script>

View File

@ -12,6 +12,10 @@ export default {
prettify(text) { prettify(text) {
return text.split('_').map((t) => this.capitalize(t)).join(' ') return text.split('_').map((t) => this.capitalize(t)).join(' ')
}, },
indent(text, tabs = 1) {
return text.split('\n').map((t) => `${'\t'.repeat(tabs)}${t}`).join('\n')
},
}, },
} }
</script> </script>