@prefecthq/prefect-ui-library
Version:
This library is the Vue and Typescript component library for [Prefect 2](https://github.com/PrefectHQ/prefect) and [Prefect Cloud 2](https://www.prefect.io/cloud/). _The components and utilities in this project are not meant to be used independently_.
179 lines (140 loc) • 5.33 kB
text/typescript
import { SelectOption } from '@prefecthq/prefect-design'
import { Component } from 'vue'
import { JsonInput } from '@/components'
import { InvalidSchemaValueError } from '@/models/InvalidSchemaValueError'
import { getSchemaPropertyAttrs, getSchemaPropertyComponentWithDefaultProps, getSchemaPropertyDefaultValidators, schemaPropertyComponentWithProps, SchemaPropertyComponentWithProps } from '@/services/schemas/utilities'
import { schemaHas, SchemaProperty, SchemaPropertyInputAttrs, SchemaPropertyMeta, SchemaValue } from '@/types/schemas'
import { Require } from '@/types/utilities'
import { sameValue } from '@/utilities'
import { isNumberArray, isStringArray } from '@/utilities/arrays'
import { fieldRules, isJson, ValidationMethod, ValidationMethodFactory } from '@/utilities/validation'
export type SchemaPropertyServiceSource = {
property: SchemaProperty,
level: number,
}
export type SchemaPropertyServiceConstructor = new (source: SchemaPropertyServiceSource) => SchemaPropertyService
export abstract class SchemaPropertyService {
/**
* Converts a schema value from the ui into the correct value for the api
*/
protected abstract request(value: SchemaValue): SchemaValue
/**
* Converts a schema value from the api into the correct value for the ui
*/
protected abstract response(value: SchemaValue): SchemaValue
/**
* Returns the vue component and any props necessary to render the property in the schema form
*/
protected abstract get component(): SchemaPropertyComponentWithProps
/**
* Returns the value needed for the @property {PropertyComponentWithProps} property to be
* rendered when no value exists or the value is invalid
*/
protected abstract get default(): SchemaValue
/**
* Can be extended to add property specific validation rules. Implemented here because this is not required
*/
protected get validators(): ValidationMethodFactory[] {
return []
}
/**
* Can be extended to add property specific attrs rules. Implemented here because this is not required
*/
protected get attrs(): SchemaPropertyInputAttrs {
return {}
}
protected property: SchemaProperty
protected level: number
protected withProps = schemaPropertyComponentWithProps
public constructor({ property, level }: SchemaPropertyServiceSource) {
this.property = property
this.level = level
}
public mapResponseValue(value: SchemaValue): SchemaValue {
try {
return this.response(value)
} catch (error) {
if (!(error instanceof InvalidSchemaValueError)) {
console.error(error)
}
}
try {
return this.response(this.default)
} catch (error) {
if (!(error instanceof InvalidSchemaValueError)) {
console.error(error)
}
}
return this.default
}
public mapRequestValue(value: SchemaValue): SchemaValue | undefined {
if (this.isDefaultValue(value)) {
return undefined
}
return this.request(value)
}
public getDefaultValue(): SchemaValue {
return this.default
}
public getComponent(): SchemaPropertyComponentWithProps {
if (this.component === null) {
return this.component
}
return getSchemaPropertyComponentWithDefaultProps(this.component)
}
public getValidators(required: boolean): ValidationMethod[] {
const { title = 'Property' } = this.property
const defaults = getSchemaPropertyDefaultValidators(this.property, required)
const validators = fieldRules(title, ...this.validators)
if (this.componentIs(JsonInput)) {
validators.push(isJson(title))
}
return [...validators, ...defaults]
}
public getAttrs(): SchemaPropertyInputAttrs {
const defaults = getSchemaPropertyAttrs(this.property)
return { ...this.attrs, ...defaults }
}
public getMeta(required: boolean): SchemaPropertyMeta | null {
const { component, props } = this.getComponent() ?? {}
return {
component,
props,
required,
attrs: this.getAttrs(),
validators: this.getValidators(required),
}
}
protected componentIs(component: Component): boolean {
return this.component?.component === component
}
protected invalid(): void {
throw new InvalidSchemaValueError()
}
protected has<T extends keyof SchemaProperty>(key: T): this is { property: SchemaProperty & Require<SchemaProperty, T> } {
return schemaHas(this.property, key)
}
protected getSelectOptions(): SelectOption[] {
const options: SelectOption[] = []
const propertyEnum = this.property.enum
const itemsEnum = this.property.items?.enum
if (!propertyEnum && !itemsEnum) {
return options
}
if (!this.property.meta?.required && !this.property.default) {
options.push({ label: 'None', value: null })
}
if (isStringArray(propertyEnum) || isNumberArray(propertyEnum)) {
const mapped = propertyEnum.map<SelectOption>(value => ({ label: `${value}`, value }))
options.push(...mapped)
}
if (isStringArray(itemsEnum) || isNumberArray(itemsEnum)) {
const mapped = itemsEnum.map<SelectOption>(value => ({ label: `${value}`, value }))
options.push(...mapped)
}
return options
}
protected isDefaultValue(value: SchemaValue): boolean {
return sameValue(value, this.default)
}
}