feat(frontend): support array of objects in schema (#4106)

* feat(frontend): Support array of objets

* feat(frontend): add support for LightweightArgInput

* feat(frontend): support objects and arrays of objects in LightweightArgInput
This commit is contained in:
Faton Ramadani
2024-07-30 11:42:49 +02:00
committed by GitHub
parent 6a33624416
commit 5992b82180
6 changed files with 83 additions and 12 deletions
+1
View File
@@ -30,6 +30,7 @@ export interface SchemaProperty {
contentEncoding?: 'base64'
enum?: string[]
resourceType?: string
properties?: { [name: string]: SchemaProperty }
}
min?: number
max?: number
+20 -3
View File
@@ -1,6 +1,11 @@
<script lang="ts">
import type { EnumType, SchemaProperty } from '$lib/common'
import { setInputCat as computeInputCat, debounce, emptyString } from '$lib/utils'
import {
setInputCat as computeInputCat,
debounce,
emptyString,
getSchemaFromProperties
} from '$lib/utils'
import { DollarSign, Pipette, Plus, X } from 'lucide-svelte'
import { createEventDispatcher, tick } from 'svelte'
import Multiselect from 'svelte-multiselect'
@@ -54,6 +59,7 @@
enum?: string[]
multiselect?: string[]
resourceType?: string
properties?: { [name: string]: SchemaProperty }
}
| undefined = undefined
@@ -381,7 +387,7 @@
on:change={(x) => fileChanged(x, (val) => (value[i] = val))}
multiple={false}
/>
{:else if itemsType?.type == 'object' && itemsType?.resourceType === undefined}
{:else if itemsType?.type == 'object' && itemsType?.resourceType === undefined && itemsType?.properties === undefined}
<JsonEditor code={JSON.stringify(v, null, 2)} bind:value={v} />
{:else if Array.isArray(itemsType?.enum)}
<ArgEnum
@@ -403,6 +409,17 @@
/>
{:else if itemsType?.type == 'resource' && itemsType?.resourceType}
<ResourcePicker bind:value={v} resourceType={itemsType?.resourceType} />
{:else if itemsType?.type === 'object' && itemsType?.properties}
<div class="p-8 border rounded-md w-full">
<SchemaForm
{onlyMaskPassword}
{disablePortal}
{disabled}
noDelete
schema={getSchemaFromProperties(itemsType?.properties)}
bind:args={v}
/>
</div>
{:else}
<input type="text" bind:value={v} id="arg-input-array" />
{/if}
@@ -623,7 +640,7 @@
/>
{/if}
</div>
{:else if properties && Object.keys(properties).length > 0}
{:else if properties && Object.keys(properties).length > 0 && inputCat !== 'list'}
<div class="p-4 pl-8 border rounded-md w-full">
{#if orderEditable}
<SchemaFormDnd
@@ -56,7 +56,7 @@
<option value="bytes">Items are bytes</option>
</select>
</Label>
{:else}
{:else if itemsType?.resourceType}
<Label label="Resource type">
<Badge color="blue">
{itemsType?.resourceType}
@@ -1,5 +1,5 @@
<script lang="ts">
import { setInputCat as computeInputCat, emptyString } from '$lib/utils'
import { setInputCat as computeInputCat, emptyString, getSchemaFromProperties } from '$lib/utils'
import { Button } from './common'
import { createEventDispatcher, tick } from 'svelte'
import FieldHeader from './FieldHeader.svelte'
@@ -45,6 +45,7 @@
enum?: string[]
multiselect?: string[]
resourceType?: string
properties?: { [name: string]: SchemaProperty }
}
| undefined = undefined
export let displayHeader = true
@@ -115,9 +116,14 @@
}
}
// Only used for object inputCat
export function evalValueToRaw() {
if (value) {
rawValue = JSON.stringify(value, null, 4)
} else {
// If value is undefined, set rawValue to empty object
// This is to prevent the textarea from being empty
rawValue = '{}'
}
}
@@ -202,6 +208,18 @@
valid = true
error = ''
}
function addItemByItemsType() {
if (value == undefined || !Array.isArray(value)) {
value = []
}
if (itemsType?.type === 'object') {
value = value.concat({})
} else {
value = value.concat('')
}
}
</script>
{#if render}
@@ -335,12 +353,19 @@
bind:value={v}
resourceType={itemsType?.resourceType}
/>
{:else if itemsType?.type === 'object' && itemsType?.properties}
<div class="p-8 border rounded-md w-full">
<LightweightSchemaForm
schema={getSchemaFromProperties(itemsType?.properties)}
bind:args={v}
/>
</div>
{:else}
<input type="text" bind:value={v} />
{/if}
<button
transition:fade|local={{ duration: 100 }}
class="rounded-full p-1 bg-surface-secondary duration-200 hover:bg-surface-hover ml-2"
class="rounded-full p-1 bg-surface-secondary duration-200 hover:bg-surface-hover ml-2 flex items-center h-6"
aria-label="Clear"
on:click={() => {
value = value.filter((el) => el != v)
@@ -364,10 +389,7 @@
size="sm"
btnClasses="mt-1"
on:click={() => {
if (value == undefined || !Array.isArray(value)) {
value = []
}
value = value.concat('')
addItemByItemsType()
}}
startIcon={{ icon: Plus }}
>
+22 -1
View File
@@ -4,8 +4,14 @@ export function argSigToJsonSchemaType(
t:
| string
| { resource: string | null }
| {
list:
| (string | { object: { key: string; typ: any }[] })
| { str: any }
| { object: { key: string; typ: any }[] }
| null
}
| { dynselect: string }
| { list: string | { str: any } | { object: { key: string; typ: any }[] } | null }
| { str: string[] | null }
| { object: { key: string; typ: any }[] }
| {
@@ -107,6 +113,21 @@ export function argSigToJsonSchemaType(
newS.items = { type: 'string', enum: t.list.str }
} else if (t.list && typeof t.list == 'object' && 'resource' in t.list && t.list.resource) {
newS.items = { type: 'resource', resourceType: t.list.resource as string }
} else if (
t.list &&
typeof t.list == 'object' &&
'object' in t.list &&
t.list.object &&
t.list.object.length > 0
) {
const properties = {}
for (const prop of t.list.object) {
properties[prop.key] = { description: '', type: '' }
argSigToJsonSchemaType(prop.typ, properties[prop.key])
}
newS.items = { type: 'object', properties: properties }
} else {
newS.items = { type: 'object' }
}
+11 -1
View File
@@ -12,7 +12,7 @@ import YAML from 'yaml'
import type { UserExt } from './stores'
import { sendUserToast } from './toast'
import type { Script } from './gen'
import type { EnumType } from './common'
import type { EnumType, SchemaProperty } from './common'
import type { Schema } from './common'
export { sendUserToast }
@@ -927,3 +927,13 @@ export function shouldDisplayPlaceholder(
return type === undefined
}
export function getSchemaFromProperties(properties: { [name: string]: SchemaProperty }): Schema {
return {
properties: Object.fromEntries(Object.entries(properties).filter(([k, v]) => k !== 'label')),
required: Object.keys(properties).filter((k) => properties[k].required),
$schema: '',
type: 'object',
order: Object.keys(properties).filter((k) => k !== 'label')
}
}