import { Map, List, OrderedMap } from "immutable"; import { CollectionReference, EnumReference, } from "../../collection/domains/reference/state"; import { CollectionSelection } from "../../collection/domains/selection/state"; import { BasicFun } from "../../../../fun/state"; import { ValueOrErrors } from "../../../../collections/domains/valueOrErrors/state"; import { PredicateValue, replaceWith, Sum, Unit, ValueRecord, unit, ValueTuple, DispatchInfiniteStreamSources, DispatchInjectedPrimitives, RecordAbstractRendererState, TableAbstractRendererState, MapRepo, ValueTable, DispatchCommonFormState, UnionAbstractRendererState, OneAbstractRendererState, DispatchLookupSources, DispatchTableApiSources, ValueOption, BasicUpdater, Option, CommonAbstractRendererState, EnumMultiselectAbstractRendererView, RecordAbstractRendererView, SearchableInfiniteStreamMultiselectAbstractRendererView, OneAbstractRendererView, ReadOnlyAbstractRendererView, TableAbstractRendererView, UnionAbstractRendererView, DispatchInjectablesTypes, SpecificationApis, ValueFilterContains, ValueFilterEqualsTo, ValueFilterGreaterThanOrEqualsTo, ValueFilterGreaterThan, ValueFilterIsNotNull, ValueFilterIsNull, ValueFilterSmallerThanOrEqualsTo, ValueFilterSmallerThan, ValueFilterStartsWith, ValueSumN, ValueFilterNotEqualsTo, } from "../../../../../main"; import { DispatchDelta } from "../runner/domains/deltas/dispatch-delta/state"; import { DispatchParsedType, DispatchTypeName, } from "../deserializer/domains/specification/domains/types/state"; import { UnitAbstractRendererState, UnitAbstractRendererView, } from "../runner/domains/abstract-renderers/unit/state"; import { StringAbstractRendererState, StringAbstractRendererView, } from "../runner/domains/abstract-renderers/string/state"; import { NumberAbstractRendererState, NumberAbstractRendererView, } from "../runner/domains/abstract-renderers/number/state"; import { BoolAbstractRendererState, BoolAbstractRendererView, } from "../runner/domains/abstract-renderers/boolean/state"; import { DateAbstractRendererState, DateAbstractRendererView, } from "../runner/domains/abstract-renderers/date/state"; import { Base64FileAbstractRendererState, Base64FileAbstractRendererView, } from "../runner/domains/abstract-renderers/base-64-file/state"; import { SecretAbstractRendererState, SecretAbstractRendererView, } from "../runner/domains/abstract-renderers/secret/state"; import { ReadOnlyAbstractRendererState } from "../runner/domains/abstract-renderers/readOnly/state"; import { MapAbstractRendererState, MapAbstractRendererView, } from "../runner/domains/abstract-renderers/map/state"; import { TupleAbstractRendererState, TupleAbstractRendererView, } from "../runner/domains/abstract-renderers/tuple/state"; import { SumAbstractRendererState, SumAbstractRendererView, } from "../runner/domains/abstract-renderers/sum/state"; import { EnumAbstractRendererState, EnumAbstractRendererView, } from "../runner/domains/abstract-renderers/enum/state"; import { ListAbstractRendererState, ListAbstractRendererView, } from "../runner/domains/abstract-renderers/list/state"; import { SearchableInfiniteStreamAbstractRendererState, SearchableInfiniteStreamAbstractRendererView, } from "../runner/domains/abstract-renderers/searchable-infinite-stream/state"; import { Renderer } from "../deserializer/domains/specification/domains/forms/domains/renderer/state"; import { LookupRenderer } from "../deserializer/domains/specification/domains/forms/domains/renderer/domains/lookup/state"; const sortObjectKeys = (obj: Record) => Object.keys(obj) .sort() .reduce((sortedObj, key) => { sortedObj[key] = obj[key]!; return sortedObj; }, {} as any); const simpleMapKeyToIdentifer = (key: any): string => { if (typeof key == "object") return JSON.stringify(sortObjectKeys(key)); return JSON.stringify(key); }; export type VoidCallbackWithOptionalFlags = ( flags: Flags | undefined, ) => void; export type ValueCallbackWithOptionalFlags = ( value: Value, flags: Flags | undefined, ) => void; export type DispatchOnChangeResult = { comparand: string; }; export type DispatchOnChange = ( updater: Option>, delta: DispatchDelta, ) => Promise; type ApiConverter = { fromAPIRawValue: BasicFun; toAPIRawValue: BasicFun<[T, boolean], any>; }; export type DispatchApiConverters> = { [key in keyof T]: ApiConverter; } & BuiltInApiConverters; type RawUnion = { caseName: string; fields: Record; }; type Table = { data: Map; hasMoreValues: boolean; from: number; to: number; defaultRow: Row; }; export const DispatchGenericTypes = [ "SingleSelection", "MultiSelection", "List", "Map", "Union", "Tuple", "Option", "Sum", "KeyOf", "Table", "One", "ReadOnly", ] as const; export type DispatchGenericType = (typeof DispatchGenericTypes)[number]; type BuiltInApiConverters = { unit?: ApiConverter; string: ApiConverter; number: ApiConverter; boolean: ApiConverter; base64File: ApiConverter; secret: ApiConverter; Date: ApiConverter; union: ApiConverter; SingleSelection: ApiConverter< CollectionSelection >; MultiSelection: ApiConverter< OrderedMap >; List: ApiConverter>; Map: ApiConverter>; Tuple: ApiConverter>; Sum: ApiConverter>; SumN: ApiConverter; SumUnitDate: ApiConverter>; Table: ApiConverter>; One: ApiConverter; ReadOnly: ApiConverter; Contains: ApiConverter; "=": ApiConverter; "!=": ApiConverter; ">=": ApiConverter; ">": ApiConverter; "!=null": ApiConverter; "=null": ApiConverter; "<=": ApiConverter; "<": ApiConverter; StartsWith: ApiConverter; Record: ApiConverter>; }; export type ConcreteRenderers< T extends DispatchInjectablesTypes, Flags = Unit, CustomPresentationContext = Unit, ExtraContext = Unit, > = { unit: { [_: string]: () => | UnitAbstractRendererView | React.MemoExoticComponent< UnitAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > >; }; boolean: { [_: string]: () => | BoolAbstractRendererView | React.MemoExoticComponent< BoolAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > >; }; number: { [_: string]: () => | NumberAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > | React.MemoExoticComponent< NumberAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > >; }; string: { [_: string]: () => | StringAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > | React.MemoExoticComponent< StringAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > >; }; base64File: { [_: string]: () => | Base64FileAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > | React.MemoExoticComponent< Base64FileAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > >; }; secret: { [_: string]: () => | SecretAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > | React.MemoExoticComponent< SecretAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > >; }; date: { [_: string]: () => | DateAbstractRendererView | React.MemoExoticComponent< DateAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > >; }; enumSingleSelection: { [_: string]: () => | EnumAbstractRendererView | React.MemoExoticComponent< EnumAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > >; }; enumMultiSelection: { [_: string]: () => | EnumMultiselectAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > | React.MemoExoticComponent< EnumMultiselectAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > >; }; streamSingleSelection: { [_: string]: () => | SearchableInfiniteStreamAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > | React.MemoExoticComponent< SearchableInfiniteStreamAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > >; }; streamMultiSelection: { [_: string]: () => | SearchableInfiniteStreamMultiselectAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > | React.MemoExoticComponent< SearchableInfiniteStreamMultiselectAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > >; }; list: { [_: string]: () => | ListAbstractRendererView | React.MemoExoticComponent< ListAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > >; }; map: { [_: string]: () => | MapAbstractRendererView | React.MemoExoticComponent< MapAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > >; }; tuple: { [_: string]: () => | TupleAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > | React.MemoExoticComponent< TupleAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > >; }; sum: { [_: string]: () => | SumAbstractRendererView | React.MemoExoticComponent< SumAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > >; }; sumUnitDate: { [_: string]: () => | SumAbstractRendererView | React.MemoExoticComponent< SumAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > >; }; record: { [_: string]: () => | RecordAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > | React.MemoExoticComponent< RecordAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > >; }; table: { [_: string]: () => | TableAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > | React.MemoExoticComponent< TableAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > >; }; union: { [_: string]: () => | UnionAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > | React.MemoExoticComponent< UnionAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > >; }; one: { [_: string]: () => | OneAbstractRendererView | React.MemoExoticComponent< OneAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > >; }; readOnly: { [_: string]: () => | ReadOnlyAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > | React.MemoExoticComponent< ReadOnlyAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > >; }; } & { [key in keyof T]: { [_: string]: () => T[key]["view"] }; }; export type ConcreteRenderer = | Base64FileAbstractRendererView | BoolAbstractRendererView | DateAbstractRendererView | EnumAbstractRendererView | EnumMultiselectAbstractRendererView | ListAbstractRendererView | MapAbstractRendererView | NumberAbstractRendererView | OneAbstractRendererView | ReadOnlyAbstractRendererView | RecordAbstractRendererView | SearchableInfiniteStreamAbstractRendererView | SearchableInfiniteStreamMultiselectAbstractRendererView | SecretAbstractRendererView | StringAbstractRendererView | SumAbstractRendererView | TableAbstractRendererView | TupleAbstractRendererView | UnionAbstractRendererView | UnitAbstractRendererView; export const concreteRendererToKind = < T extends DispatchInjectablesTypes, Flags, CustomPresentationContext, ExtraContext = Unit, >( concreteRenderers: ConcreteRenderers< T, Flags, CustomPresentationContext, ExtraContext >, ) => (name: string): ValueOrErrors => { const viewTypes = Object.keys(concreteRenderers); for (const viewType of viewTypes) { if (name in concreteRenderers[viewType as keyof ConcreteRenderers]) { return ValueOrErrors.Default.return(viewType); } } return ValueOrErrors.Default.throwOne( `cannot find view ${name} in concrete renderers`, ); }; export const tryGetConcreteRenderer = < T extends DispatchInjectablesTypes, Flags, CustomPresentationContext, ExtraContext, >( concreteRenderers: ConcreteRenderers< T, Flags, CustomPresentationContext, ExtraContext >, ) => < K extends keyof ConcreteRenderers< T, Flags, CustomPresentationContext, ExtraContext >, >( kind: K, name: keyof ConcreteRenderers< T, Flags, CustomPresentationContext, ExtraContext >[K], ): ValueOrErrors< ReturnType< ConcreteRenderers< T, Flags, CustomPresentationContext, ExtraContext >[K][keyof ConcreteRenderers< T, Flags, CustomPresentationContext, ExtraContext >[K]] >, string > => { if (!concreteRenderers[kind]) { return ValueOrErrors.Default.throwOne( `cannot find concrete renderer kind "${kind as string}" in concrete renderers`, ); } if (concreteRenderers[kind][name]) { return ValueOrErrors.Default.return(concreteRenderers[kind][name]()); } return ValueOrErrors.Default.throwOne( `cannot find concrete renderer "${name as string}" in kind "${kind as string}"`, ); }; export const getDefaultRecordRenderer = < CustomPresentationContext, Flags, ExtraContext, >( isNested: boolean, defaultRecordRenderer: () => RecordAbstractRendererView< CustomPresentationContext, Flags, ExtraContext >, defaultNestedRecordRenderer: () => RecordAbstractRendererView< CustomPresentationContext, Flags, ExtraContext >, ): RecordAbstractRendererView< CustomPresentationContext, Flags, ExtraContext > => (isNested ? defaultNestedRecordRenderer() : defaultRecordRenderer()); export const dispatchDefaultState = >( infiniteStreamSources: DispatchInfiniteStreamSources, injectedPrimitives: DispatchInjectedPrimitives | undefined, types: Map>, forms: Map>, converters: DispatchApiConverters, lookupSources: DispatchLookupSources | undefined, tableApiSources: DispatchTableApiSources | undefined, specApis: SpecificationApis, ) => ( t: DispatchParsedType, renderer: Renderer, ): ValueOrErrors => { const result: ValueOrErrors = (() => { if (renderer == undefined) { return ValueOrErrors.Default.return(undefined); } if (renderer.kind == "lookupType-lookupRenderer") { return DispatchParsedType.Operations.ResolveLookupType( renderer.type.name, types, ).Then((resolvedType) => LookupRenderer.Operations.ResolveRenderer(renderer, forms).Then( (resolvedRenderer) => dispatchDefaultState( infiniteStreamSources, injectedPrimitives, types, forms, converters, lookupSources, tableApiSources, specApis, )(resolvedType, resolvedRenderer), ), ); } if (renderer.kind == "lookupType-inlinedRenderer") { return DispatchParsedType.Operations.ResolveLookupType( renderer.type.name, types, ).Then((resolvedType) => dispatchDefaultState( infiniteStreamSources, injectedPrimitives, types, forms, converters, lookupSources, tableApiSources, specApis, )(resolvedType, renderer.inlinedRenderer), ); } if (renderer.kind == "inlinedType-lookupRenderer") { return LookupRenderer.Operations.ResolveRenderer(renderer, forms).Then( (resolvedRenderer) => dispatchDefaultState( infiniteStreamSources, injectedPrimitives, types, forms, converters, lookupSources, tableApiSources, specApis, )(renderer.type, resolvedRenderer), ); } if (t.kind == "primitive") return t.name == "unit" ? ValueOrErrors.Default.return(UnitAbstractRendererState.Default()) : t.name == "boolean" ? ValueOrErrors.Default.return(BoolAbstractRendererState.Default()) : t.name == "number" ? ValueOrErrors.Default.return( NumberAbstractRendererState.Default(), ) : t.name == "string" || t.name == "entityIdString" || t.name == "calculatedDisplayValue" ? ValueOrErrors.Default.return( StringAbstractRendererState.Default(), ) : t.name == "base64File" ? ValueOrErrors.Default.return( Base64FileAbstractRendererState.Default(), ) : t.name == "secret" ? ValueOrErrors.Default.return( SecretAbstractRendererState.Default(), ) : t.name == "Date" ? ValueOrErrors.Default.return( DateAbstractRendererState.Default(), ) : injectedPrimitives?.get(t.name as keyof T) != undefined ? ValueOrErrors.Default.return({ commonFormState: DispatchCommonFormState.Default(), lastOnChangePromise: Option.Default.none(), ...injectedPrimitives?.get(t.name as keyof T)! .defaultState, }) : ValueOrErrors.Default.throwOne( `could not resolve defaultState for primitive renderer kind "${ t.name as string }"`, ); if (t.kind == "singleSelection") return renderer.kind == "enumRenderer" ? ValueOrErrors.Default.return(EnumAbstractRendererState().Default()) : renderer.kind == "streamRenderer" ? infiniteStreamSources(renderer.stream).Then((streamSource) => ValueOrErrors.Default.return( SearchableInfiniteStreamAbstractRendererState.Default( streamSource, ), ), ) : ValueOrErrors.Default.throwOne( `received non singleSelection renderer kind "${renderer.kind}" when resolving defaultState for singleSelection`, ); if (t.kind == "multiSelection") return renderer.kind == "enumRenderer" ? ValueOrErrors.Default.return(EnumAbstractRendererState().Default()) : renderer.kind == "streamRenderer" ? infiniteStreamSources(renderer.stream).Then((streamSource) => ValueOrErrors.Default.return( SearchableInfiniteStreamAbstractRendererState.Default( streamSource, ), ), ) : ValueOrErrors.Default.throwOne( `received non multiSelection renderer kind "${renderer.kind}" when resolving defaultState for multiSelection`, ); if (t.kind == "list") return renderer.kind == "listRenderer" ? ValueOrErrors.Default.return( ListAbstractRendererState.Default.zero(), ) : ValueOrErrors.Default.throwOne( `received non list renderer kind "${renderer.kind}" when resolving defaultState for list`, ); if (t.kind == "map") return renderer.kind == "mapRenderer" ? ValueOrErrors.Default.return( MapAbstractRendererState.Default.zero(), ) : ValueOrErrors.Default.throwOne( `received non map renderer kind "${renderer.kind}" when resolving defaultState for map`, ); if (t.kind == "tuple") return renderer.kind == "tupleRenderer" ? ValueOrErrors.Operations.All( List< ValueOrErrors<[number, CommonAbstractRendererState], string> >( t.args.map((_, index) => dispatchDefaultState( infiniteStreamSources, injectedPrimitives, types, forms, converters, lookupSources, tableApiSources, specApis, )(_, renderer.itemRenderers[index].renderer).Then( (itemState) => ValueOrErrors.Default.return([index, itemState]), ), ), ), ).Then((itemStates) => ValueOrErrors.Default.return( TupleAbstractRendererState.Default(Map(itemStates)), ), ) : ValueOrErrors.Default.throwOne( `received non tuple renderer kind "${renderer.kind}" when resolving defaultState for tuple`, ); if (t.kind == "sum") return renderer.kind == "sumRenderer" ? dispatchDefaultState( infiniteStreamSources, injectedPrimitives, types, forms, converters, lookupSources, tableApiSources, specApis, )(t.args[0], renderer.leftRenderer.renderer).Then((left) => renderer.rightRenderer == undefined ? ValueOrErrors.Default.throwOne( `rightRenderer is undefined when resolving defaultState sum view ${renderer.concreteRenderer}`, ) : dispatchDefaultState( infiniteStreamSources, injectedPrimitives, types, forms, converters, lookupSources, tableApiSources, specApis, )(t.args[1], renderer.rightRenderer.renderer).Then((right) => ValueOrErrors.Default.return( SumAbstractRendererState.Default({ left, right, }), ), ), ) : renderer.kind == "sumUnitDateRenderer" ? ValueOrErrors.Default.return( SumAbstractRendererState.Default({ left: UnitAbstractRendererState.Default(), right: DateAbstractRendererState.Default(), }), ) : ValueOrErrors.Default.throwOne( `renderer kind "${renderer.kind}" not supported for sum`, ); if (t.kind == "one") return renderer.kind != "oneRenderer" ? ValueOrErrors.Default.throwOne( `received non one renderer kind "${renderer.kind}" when resolving defaultState for one`, ) : specApis.lookups != undefined && specApis.lookups.get(renderer.api[0]) != undefined && specApis.lookups.get(renderer.api[0])?.one != undefined && specApis.lookups.get(renderer.api[0])?.one.get(renderer.api[1]) != undefined && specApis.lookups.get(renderer.api[0])?.one.get(renderer.api[1]) ?.methods.getManyUnlinked ? lookupSources == undefined ? ValueOrErrors.Default.throwOne( `lookup sources referenced but no lookup sources are provided`, ) : lookupSources(renderer.api[0]) == undefined ? ValueOrErrors.Default.throwOne( `cannot find lookup source for ${renderer.api[0]}`, ) : lookupSources(renderer.api[0]).Then((lookupSource) => lookupSource.one == undefined ? ValueOrErrors.Default.throwOne( `one source not provided for ${renderer.api[0]}`, ) : lookupSource.one!(renderer.api[1]) // safe because we check for undefined above but type system doesn't know that .Then((oneSource) => oneSource.getManyUnlinked == undefined ? ValueOrErrors.Default.throwOne( `getManyUnlinked not provided for ${renderer.api[0]}-${renderer.api[1]}`, ) : MapRepo.Operations.tryFindWithError( t.arg.name, types, () => `cannot find lookup type ${JSON.stringify( t.arg.name, )} in ${JSON.stringify(t)}`, ).Then((lookupType) => ValueOrErrors.Default.return( OneAbstractRendererState.Default( oneSource.getManyUnlinked!( // safe because we check for undefined above but type system doesn't know that dispatchFromAPIRawValue( lookupType, types, converters, injectedPrimitives, ), ), ), ), ), ), ) : ValueOrErrors.Default.return( OneAbstractRendererState.Default(undefined), ); if (t.kind == "readOnly") return renderer.kind != "readOnlyRenderer" ? ValueOrErrors.Default.throwOne( `received non readOnly renderer kind "${renderer.kind}" when resolving defaultState for readOnly`, ) : dispatchDefaultState( infiniteStreamSources, injectedPrimitives, types, forms, converters, lookupSources, tableApiSources, specApis, )(t.arg, renderer.childRenderer.renderer).Then((childState) => ValueOrErrors.Default.return( ReadOnlyAbstractRendererState.Default.childFormState( childState, ), ), ); if (t.kind == "record") return renderer.kind == "recordRenderer" ? ValueOrErrors.Operations.All( List< ValueOrErrors<[string, CommonAbstractRendererState], string> >( renderer.fields .entrySeq() .map(([fieldName, fieldRenderer]) => MapRepo.Operations.tryFindWithError( fieldName, t.fields, () => `field ${fieldName} not found in renderer ${JSON.stringify( renderer.fields, )} fields`, ).Then((fieldType) => dispatchDefaultState( infiniteStreamSources, injectedPrimitives, types, forms, converters, lookupSources, tableApiSources, specApis, )(fieldType, fieldRenderer.renderer).Then((value) => ValueOrErrors.Default.return([fieldName, value]), ), ), ), ), ).Then((res) => ValueOrErrors.Default.return( RecordAbstractRendererState.Default.fieldState(Map(res)), ), ) : ValueOrErrors.Default.throwOne( `received non record renderer kind "${renderer.kind}" when resolving defaultValue for record`, ); if (t.kind == "union") { return renderer.kind == "unionRenderer" ? ValueOrErrors.Operations.All( List>( renderer.cases .entrySeq() .map(([caseName, caseRenderer]) => MapRepo.Operations.tryFindWithError( caseName, t.args, () => `case ${caseName} not found in type ${JSON.stringify( t, )}`, ).Then((caseType) => dispatchDefaultState( infiniteStreamSources, injectedPrimitives, types, forms, converters, lookupSources, tableApiSources, specApis, )(caseType, caseRenderer).Then((caseState) => ValueOrErrors.Default.return([caseName, caseState]), ), ), ), ), ).Then((caseStates) => ValueOrErrors.Default.return( UnionAbstractRendererState.Default(Map(caseStates)), ), ) : ValueOrErrors.Default.throwOne( `received non union renderer kind "${renderer.kind}" when resolving defaultState for union`, ); } if (t.kind == "table") { return renderer.kind == "tableRenderer" ? ValueOrErrors.Default.return(TableAbstractRendererState.Default()) : ValueOrErrors.Default.throwOne( `received non table renderer kind "${renderer.kind}" when resolving defaultState for table`, ); } return ValueOrErrors.Default.throwOne( `type of kind "${JSON.stringify(t)}" not supported by defaultState`, ); })(); return result.MapErrors((errors) => errors.map( (error) => `${error}\n...When resolving defaultState for ${t.kind} and renderer kind ${renderer.kind}`, ), ); }; export const dispatchDefaultValue = >( injectedPrimitives: DispatchInjectedPrimitives | undefined, types: Map>, forms: Map>, ) => ( t: DispatchParsedType, renderer: Renderer, ): ValueOrErrors => { const result: ValueOrErrors = (() => { if (renderer == undefined) { return ValueOrErrors.Default.return(PredicateValue.Default.unit()); } if (renderer.kind == "lookupType-lookupRenderer") return DispatchParsedType.Operations.ResolveLookupType( renderer.type.name, types, ).Then((resolvedType) => LookupRenderer.Operations.ResolveRenderer(renderer, forms).Then( (resolvedRenderer) => dispatchDefaultValue( injectedPrimitives, types, forms, )(resolvedType, resolvedRenderer), ), ); if (renderer.kind == "lookupType-inlinedRenderer") return DispatchParsedType.Operations.ResolveLookupType( renderer.type.name, types, ).Then((resolvedType) => dispatchDefaultValue( injectedPrimitives, types, forms, )(resolvedType, renderer.inlinedRenderer), ); if (renderer.kind == "inlinedType-lookupRenderer") return LookupRenderer.Operations.ResolveRenderer(renderer, forms).Then( (resolvedRenderer) => dispatchDefaultValue( injectedPrimitives, types, forms, )(renderer.type, resolvedRenderer), ); if (t.kind == "primitive") return t.name == "unit" ? ValueOrErrors.Default.return(PredicateValue.Default.unit()) : t.name == "boolean" ? ValueOrErrors.Default.return(PredicateValue.Default.boolean()) : t.name == "number" ? ValueOrErrors.Default.return(PredicateValue.Default.number()) : t.name == "string" ? ValueOrErrors.Default.return(PredicateValue.Default.string()) : t.name == "base64File" ? ValueOrErrors.Default.return( PredicateValue.Default.string(), ) : t.name == "secret" ? ValueOrErrors.Default.return( PredicateValue.Default.string(), ) : t.name == "Date" ? ValueOrErrors.Default.return( PredicateValue.Default.date(), ) : injectedPrimitives?.get(t.name as keyof T) != undefined ? ValueOrErrors.Default.return( injectedPrimitives?.get(t.name as keyof T)! .defaultValue, ) : ValueOrErrors.Default.throwOne( `could not resolve defaultValue for primitive renderer type "${ t.name as string }"`, ); if (t.kind == "singleSelection") return renderer.kind == "enumRenderer" || renderer.kind == "streamRenderer" ? ValueOrErrors.Default.return( PredicateValue.Default.option( false, PredicateValue.Default.unit(), ), ) : ValueOrErrors.Default.throwOne( `received non singleSelection renderer kind "${renderer.kind}" when resolving defaultValue for singleSelection`, ); if (t.kind == "multiSelection") return renderer.kind == "enumRenderer" || renderer.kind == "streamRenderer" ? ValueOrErrors.Default.return( PredicateValue.Default.record(OrderedMap()), ) : ValueOrErrors.Default.throwOne( `received non multiSelection renderer kind "${renderer.kind}" when resolving defaultValue for multiSelection`, ); if (t.kind == "list") return renderer.kind == "listRenderer" ? ValueOrErrors.Default.return(PredicateValue.Default.tuple(List())) : ValueOrErrors.Default.throwOne( `received non list renderer kind "${renderer.kind}" when resolving defaultValue for list`, ); if (t.kind == "map") return renderer.kind == "mapRenderer" ? ValueOrErrors.Default.return(PredicateValue.Default.tuple(List())) : ValueOrErrors.Default.throwOne( `received non map renderer kind "${renderer.kind}" when resolving defaultValue for map`, ); if (t.kind == "tuple") return renderer.kind == "tupleRenderer" ? ValueOrErrors.Operations.All( List>( t.args.map((_, index) => dispatchDefaultValue( injectedPrimitives, types, forms, )(_, renderer.itemRenderers[index].renderer), ), ), ).Then((values) => ValueOrErrors.Default.return( PredicateValue.Default.tuple(List(values)), ), ) : ValueOrErrors.Default.throwOne( `received non tuple renderer kind "${renderer.kind}" when resolving defaultValue for tuple`, ); if (t.kind == "sum") return renderer.kind == "sumRenderer" ? dispatchDefaultValue( injectedPrimitives, types, forms, )(t.args[0], renderer.leftRenderer.renderer).Then((left) => ValueOrErrors.Default.return( PredicateValue.Default.sum(Sum.Default.left(left)), ), ) : renderer.kind == "sumUnitDateRenderer" ? ValueOrErrors.Default.return( PredicateValue.Default.sum( Sum.Default.left(PredicateValue.Default.unit()), ), ) : ValueOrErrors.Default.throwOne( `received non sum renderer kind "${renderer.kind}" when resolving defaultValue for sum`, ); if (t.kind == "one") { return renderer.kind == "oneRenderer" ? ValueOrErrors.Default.return( PredicateValue.Default.option( false, PredicateValue.Default.unit(), ), ) : ValueOrErrors.Default.throwOne( `received non one renderer kind "${renderer.kind}" when resolving defaultValue for one`, ); } if (t.kind == "readOnly") { return renderer.kind == "readOnlyRenderer" ? dispatchDefaultValue( injectedPrimitives, types, forms, )(t.arg, renderer.childRenderer.renderer).Then((childValue) => ValueOrErrors.Default.return( PredicateValue.Default.readonly(childValue), ), ) : ValueOrErrors.Default.throwOne( `received non readOnly renderer kind "${renderer.kind}" when resolving defaultValue for readOnly`, ); } if (t.kind == "record") return renderer.kind == "recordRenderer" ? ValueOrErrors.Operations.All( List>( renderer.fields .entrySeq() .map(([fieldName, fieldRenderer]) => MapRepo.Operations.tryFindWithError( fieldName, t.fields, () => `field ${fieldName} not found in type ${JSON.stringify( t, )} fields`, ).Then((fieldType) => dispatchDefaultValue( injectedPrimitives, types, forms, )(fieldType, fieldRenderer.renderer).Then((value) => ValueOrErrors.Default.return([fieldName, value]), ), ), ), ), ).Then((res) => ValueOrErrors.Default.return( PredicateValue.Default.record(OrderedMap(res)), ), ) : ValueOrErrors.Default.throwOne( `received non record renderer kind "${renderer.kind}" when resolving defaultValue for record`, ); if (t.kind == "union") { return renderer.kind != "unionRenderer" ? ValueOrErrors.Default.throwOne( `received non union renderer kind "${renderer.kind}" when resolving defaultValue for union`, ) : MapRepo.Operations.tryFirstWithError( t.args, () => `union type ${JSON.stringify(t, null, 2)} has no cases`, ).Then((firstCaseType) => MapRepo.Operations.tryFirstWithError( renderer.cases, () => `union renderer has no cases`, ).Then((firstCaseRenderer) => { const defaultCase = renderer.cases.keySeq().first(); if (defaultCase == undefined) return ValueOrErrors.Default.throwOne( "No union case can be found", ); return dispatchDefaultValue( injectedPrimitives, types, forms, )(firstCaseType, firstCaseRenderer).Then((result) => ValueOrErrors.Operations.Return( PredicateValue.Default.unionCase(defaultCase, result), ), ); }), ); } if (t.kind == "table") { return renderer.kind == "tableRenderer" ? ValueOrErrors.Default.return( PredicateValue.Default.table( 0, 0, Map(), false, ValueRecord.Default.empty(), ), ) : ValueOrErrors.Default.throwOne( `received non table renderer kind "${renderer.kind}" when resolving defaultValue for table`, ); } return ValueOrErrors.Default.throwOne( `type ${t} not supported by defaultValue`, ); })(); return result.MapErrors((errors) => errors.map( (error) => `${error}\n...When resolving defaultValue for type "${JSON.stringify(t, null, 2)}" and renderer kind "${renderer.kind}"`, ), ); }; export const dispatchFromAPIRawValue = >( t: DispatchParsedType, types: Map>, converters: DispatchApiConverters, injectedPrimitives?: DispatchInjectedPrimitives, ) => (raw: any): ValueOrErrors => { const result: ValueOrErrors = (() => { if (t.kind == "primitive") { // unit is a special kind of primitive if (t.name == "unit" && !converters[t.name]) { return ValueOrErrors.Default.return(PredicateValue.Default.unit()); } return ValueOrErrors.Default.return( converters[t.name].fromAPIRawValue(raw), ); } if (t.kind == "union") { const result = converters["union"].fromAPIRawValue(raw); const caseType = t.args.get(result.caseName); if (caseType == undefined) return ValueOrErrors.Default.throwOne( `union case ${result.caseName} not found in type ${JSON.stringify( t, )}`, ); return dispatchFromAPIRawValue( caseType, types, converters, injectedPrimitives, )(result.fields).Then((value) => ValueOrErrors.Default.return( PredicateValue.Default.unionCase(result.caseName, value), ), ); } if (t.kind == "singleSelection") { const result = converters["SingleSelection"].fromAPIRawValue(raw); const isSome = result.kind == "l"; const value = isSome ? PredicateValue.Default.record(OrderedMap(result.value)) : PredicateValue.Default.unit(); return ValueOrErrors.Default.return( PredicateValue.Default.option(isSome, value), ); } if (t.kind == "multiSelection") { const result = converters["MultiSelection"].fromAPIRawValue(raw); const values = result.map((_) => PredicateValue.Default.record(OrderedMap(_)), ); return ValueOrErrors.Default.return( PredicateValue.Default.record(OrderedMap(values)), ); } if (t.kind == "list") { const result = converters["List"].fromAPIRawValue(raw); return ValueOrErrors.Operations.All( result.map((_) => dispatchFromAPIRawValue( t.args[0], types, converters, injectedPrimitives, )(_), ), ).Then((values) => ValueOrErrors.Default.return(PredicateValue.Default.tuple(values)), ); } if (t.kind == "map" && t.args.length == 2) { const result = converters["Map"].fromAPIRawValue(raw); return ValueOrErrors.Operations.All( List>( result.map((_) => dispatchFromAPIRawValue( t.args[0], types, converters, injectedPrimitives, )(_[0]).Then((key) => dispatchFromAPIRawValue( t.args[1], types, converters, injectedPrimitives, )(_[1]).Then((value) => ValueOrErrors.Default.return( PredicateValue.Default.tuple(List([key, value])), ), ), ), ), ), ).Then((values) => ValueOrErrors.Default.return( PredicateValue.Default.tuple(List(values)), ), ); } if (t.kind == "tuple") { const result = converters["Tuple"].fromAPIRawValue(raw); return ValueOrErrors.Operations.All( List>( result.map((_, index) => dispatchFromAPIRawValue( t.args[index], types, converters, injectedPrimitives, )(_), ), ), ).Then((values) => ValueOrErrors.Default.return( PredicateValue.Default.tuple(List(values)), ), ); } if (t.kind == "sum" && t.args.length === 2) { const result = converters["Sum"].fromAPIRawValue(raw); return dispatchFromAPIRawValue( result.kind == "l" ? t.args[0] : t.args[1], types, converters, injectedPrimitives, )(result.value).Then((value) => ValueOrErrors.Default.return( PredicateValue.Default.sum( Sum.Updaters.map2(replaceWith(value), replaceWith(value))(result), ), ), ); } if (t.kind == "sumN") { const result = converters["SumN"].fromAPIRawValue(raw); return dispatchFromAPIRawValue( t.args[result.caseIndex], types, converters, injectedPrimitives, )(result.value).Then((value) => ValueOrErrors.Default.return( PredicateValue.Default.sumN(result.caseIndex, result.arity, value), ), ); } if (t.kind == "lookup") return MapRepo.Operations.tryFindWithError( t.name, // TODO -- double check this is correct instead of typeName, and maybe remove typeName types, () => `type ${t.name} not found in types`, ).Then((type) => dispatchFromAPIRawValue( type, types, converters, injectedPrimitives, )(raw), ); // TODO -- this can be more functional if (t.kind == "table") { // move to the converter if (typeof raw != "object") { console.error( `when parsing table for type ${JSON.stringify(t)}, object expected but got ${JSON.stringify(raw)}`, ); return ValueOrErrors.Default.throwOne( `object expected but got ${JSON.stringify(raw)}`, ); } const converterResult = converters["Table"].fromAPIRawValue(raw); const argType = t.arg; return DispatchParsedType.Operations.ResolveLookupType( argType.name, types, ).Then((resolvedType) => ValueOrErrors.Operations.All( List>( converterResult.data .toArray() .map(([key, record]) => dispatchFromAPIRawValue( resolvedType, types, converters, injectedPrimitives, )(record).Then((value) => PredicateValue.Operations.IsRecord(value) ? ValueOrErrors.Default.return([key, value] as const) : ValueOrErrors.Default.throwOne< [string, ValueRecord], string >( `record expected but got ${PredicateValue.Operations.GetKind( value, )}`, ), ), ), ), ).Then((values) => ValueOrErrors.Default.return( ValueTable.Default.fromParsed( converterResult.from, converterResult.to, converterResult.hasMoreValues, OrderedMap(values), converterResult.defaultRow, ), ), ), ); } if (t.kind == "one") { const result = converters["One"].fromAPIRawValue(raw); if (!result.isSome) { return ValueOrErrors.Default.return(result); } return dispatchFromAPIRawValue( t.arg, types, converters, injectedPrimitives, )(result.value).Then((value) => ValueOrErrors.Default.return( PredicateValue.Default.option(true, value), ), ); } if (t.kind == "readOnly") { const readOnlyResult = converters["ReadOnly"].fromAPIRawValue(raw); return dispatchFromAPIRawValue( t.arg, types, converters, injectedPrimitives, )(readOnlyResult.ReadOnly).Then((value) => ValueOrErrors.Default.return(PredicateValue.Default.readonly(value)), ); } // TODO -- this can be more functional if (t.kind == "record") { const converterResult = converters["Record"].fromAPIRawValue(raw); if (typeof converterResult !== "object") { console.error( `when parsing record for type ${JSON.stringify(t)}, object expected but got ${JSON.stringify(converterResult)}`, ); return ValueOrErrors.Default.throwOne( `object expected but got ${JSON.stringify(converterResult)}`, ); } let result: OrderedMap = OrderedMap(); let errors: List = List(); t.fields.forEach((fieldType, fieldName) => { const fieldValue = converterResult[fieldName]; if (fieldValue !== null && fieldValue === undefined) { return; } const parsedValue = dispatchFromAPIRawValue( fieldType, types, converters, injectedPrimitives, )(fieldValue); if (parsedValue.kind == "errors") { errors = errors.concat(parsedValue.errors); } else { result = result.set(fieldName, parsedValue.value); } }); if (errors.size > 0) { return ValueOrErrors.Default.throw(errors); } return ValueOrErrors.Default.return( PredicateValue.Default.record(result), ); } // Filters if (t.kind == "contains") { const result = converters["Contains"].fromAPIRawValue(raw); return dispatchFromAPIRawValue( t.contains, types, converters, injectedPrimitives, )(result.contains).Then((value) => ValueOrErrors.Default.return( PredicateValue.Default.filterContains(value), ), ); } if (t.kind == "=") { const result = converters["="].fromAPIRawValue(raw); return dispatchFromAPIRawValue( t.equalsTo, types, converters, injectedPrimitives, )(result.equalsTo).Then((value) => ValueOrErrors.Default.return( PredicateValue.Default.filterEqualsTo(value), ), ); } if (t.kind == "!=") { const result = converters["!="].fromAPIRawValue(raw); return dispatchFromAPIRawValue( t.notEqualsTo, types, converters, injectedPrimitives, )(result.notEqualsTo).Then((value) => ValueOrErrors.Default.return( PredicateValue.Default.filterNotEqualsTo(value), ), ); } if (t.kind == ">=") { const result = converters[">="].fromAPIRawValue(raw); return dispatchFromAPIRawValue( t.greaterThanOrEqualsTo, types, converters, injectedPrimitives, )(result.greaterThanOrEqualsTo).Then((value) => ValueOrErrors.Default.return( PredicateValue.Default.filterGreaterThanOrEqualsTo(value), ), ); } if (t.kind == ">") { const result = converters[">"].fromAPIRawValue(raw); return dispatchFromAPIRawValue( t.greaterThan, types, converters, injectedPrimitives, )(result.greaterThan).Then((value) => ValueOrErrors.Default.return( PredicateValue.Default.filterGreaterThan(value), ), ); } if (t.kind == "!=null") return ValueOrErrors.Operations.Return( converters["!=null"].fromAPIRawValue(raw), ); if (t.kind == "=null") return ValueOrErrors.Operations.Return( converters["=null"].fromAPIRawValue(raw), ); if (t.kind == "<=") { const result = converters["<="].fromAPIRawValue(raw); return dispatchFromAPIRawValue( t.smallerThanOrEqualsTo, types, converters, injectedPrimitives, )(result.smallerThanOrEqualsTo).Then((value) => ValueOrErrors.Default.return( PredicateValue.Default.filterSmallerThanOrEqualsTo(value), ), ); } if (t.kind == "<") { const result = converters["<"].fromAPIRawValue(raw); return dispatchFromAPIRawValue( t.smallerThan, types, converters, injectedPrimitives, )(result.smallerThan).Then((value) => ValueOrErrors.Default.return( PredicateValue.Default.filterSmallerThan(value), ), ); } if (t.kind == "startsWith") { const result = converters.StartsWith.fromAPIRawValue(raw); return dispatchFromAPIRawValue( t.startsWith, types, converters, injectedPrimitives, )(result.startsWith).Then((value) => ValueOrErrors.Default.return( PredicateValue.Default.filterStartsWith(value), ), ); } return ValueOrErrors.Default.throwOne( `unsupported type ${JSON.stringify(t)} for raw: `, ); })(); return result.MapErrors((errors) => errors.map( (error) => `${error}\n...When converting type ${JSON.stringify( t, null, 2, )} and value ${JSON.stringify(raw, null, 2)} from API raw value`, ), ); }; export const dispatchToAPIRawValue = >( t: DispatchParsedType, types: Map>, converters: DispatchApiConverters, injectedPrimitives?: DispatchInjectedPrimitives, ) => (raw: PredicateValue, formState: any): ValueOrErrors => { const result: ValueOrErrors = (() => { if (t.kind == "primitive") { if (t.name == "unit" && !converters[t.name]) { return ValueOrErrors.Default.return(unit); } return ValueOrErrors.Operations.Return( converters[t.name as string | keyof T].toAPIRawValue([ raw, formState?.commonFormState?.modifiedByUser ?? false, ]), ); } if (t.kind == "union") { if (!PredicateValue.Operations.IsUnionCase(raw)) { return ValueOrErrors.Default.throwOne( `Union case expected but got ${JSON.stringify(raw)}\n...when converting union to API raw value`, ); } const caseName = raw.caseName; if ( caseName == undefined || !PredicateValue.Operations.IsString(caseName) ) { return ValueOrErrors.Default.throwOne( `caseName expected but got ${JSON.stringify(raw)}`, ); } const caseType = t.args.get(caseName); if (caseType == undefined) { return ValueOrErrors.Default.throwOne( `union case ${caseName} not found in type ${JSON.stringify(t)}`, ); } return dispatchToAPIRawValue( caseType, types, converters, injectedPrimitives, )(raw.fields, formState).Then((value) => ValueOrErrors.Default.return( converters["union"].toAPIRawValue([ { caseName, fields: value }, formState?.commonFormState?.modifiedByUser ?? false, ]), ), ); } if (t.kind == "singleSelection") { if (!PredicateValue.Operations.IsOption(raw)) { return ValueOrErrors.Default.throwOne( `Option expected but got ${JSON.stringify(raw)}`, ); } if (raw.isSome) { if (!PredicateValue.Operations.IsRecord(raw.value)) { return ValueOrErrors.Default.throwOne( `Record expected but got ${JSON.stringify(raw.value)}`, ); } const rawValue = raw.value.fields.toJS(); if ( !CollectionReference.Operations.IsCollectionReference(rawValue) && !EnumReference.Operations.IsEnumReference(rawValue) ) { return ValueOrErrors.Default.throwOne( `CollectionReference or EnumReference expected but got ${rawValue}`, ); } return ValueOrErrors.Operations.Return( converters["SingleSelection"].toAPIRawValue([ Sum.Default.left(rawValue), formState?.commonFormState?.modifiedByUser ?? false, ]), ); } else { return ValueOrErrors.Operations.Return( converters["SingleSelection"].toAPIRawValue([ Sum.Default.right("no selection"), formState?.commonFormState?.modifiedByUser ?? false, ]), ); } } if (t.kind == "multiSelection") { if (!PredicateValue.Operations.IsRecord(raw)) { return ValueOrErrors.Default.throwOne( `Record expected but got multi selection of ${JSON.stringify(raw)}`, ); } const filteredRawValues = raw.fields.filter((value) => { if (!PredicateValue.Operations.IsRecord(value)) { console.warn( "Received a non-record value in a multi selection, ignoring: ", JSON.stringify(value), ); return false; } const fieldsObject = value.fields.toJS(); if ( !CollectionReference.Operations.IsCollectionReference( fieldsObject, ) && !EnumReference.Operations.IsEnumReference(fieldsObject) ) { console.warn( "Received a non-collection or enum reference value in a multi selection, ignoring: ", JSON.stringify(value), ); return false; } return true; }); const rawValue: Map< string, ValueOrErrors > = filteredRawValues.map((value) => { // should never happen due to the filter above but is a type check if (!PredicateValue.Operations.IsRecord(value)) { return ValueOrErrors.Default.throwOne( `Record expected but got ${JSON.stringify(value)}`, ); } const fieldsObject = value.fields.toJS(); if ( !CollectionReference.Operations.IsCollectionReference( fieldsObject, ) && !EnumReference.Operations.IsEnumReference(fieldsObject) ) { return ValueOrErrors.Default.throwOne( `CollectionReference or EnumReference expected but got ${JSON.stringify( fieldsObject, )}`, ); } return ValueOrErrors.Default.return(fieldsObject); }); return ValueOrErrors.Operations.All(rawValue.valueSeq().toList()).Then( (values) => ValueOrErrors.Default.return( converters["MultiSelection"].toAPIRawValue([ OrderedMap( values .map((v): [string, EnumReference | CollectionReference] => { if ( CollectionReference.Operations.IsCollectionReference(v) ) { return [v.Id, v]; } return [v.Value, v]; }) .toArray(), ), formState?.commonFormState?.modifiedByUser ?? false, ]), ), ); } if (t.kind == "list") { if (!PredicateValue.Operations.IsTuple(raw)) { return ValueOrErrors.Default.throwOne( `Tuple expected but got list of ${JSON.stringify(raw)}`, ); } return ValueOrErrors.Operations.All( List( raw.values.map((value, index) => dispatchToAPIRawValue( t.args[0], types, converters, injectedPrimitives, )(value, formState?.elementFormStates?.get(index)), ), ), ).Then((values) => ValueOrErrors.Default.return( converters["List"].toAPIRawValue([ values, formState?.commonFormState?.modifiedByUser ?? false, ]), ), ); } if (t.kind == "map" && t.args.length == 2) { const keyValues = (raw as ValueTuple).values.map((keyValue, index) => { return dispatchToAPIRawValue( t.args[0], types, converters, injectedPrimitives, )( (keyValue as ValueTuple).values.get(0)!, formState?.elementFormStates?.get(index)?.KeyFormState, ) .Then((possiblyUndefinedKey) => { if ( possiblyUndefinedKey == undefined || possiblyUndefinedKey == null || possiblyUndefinedKey == "" || (typeof possiblyUndefinedKey == "object" && (Object.keys(possiblyUndefinedKey).length == 0 || ("IsSome" in possiblyUndefinedKey && !possiblyUndefinedKey.IsSome))) ) { return ValueOrErrors.Default.throwOne( `A mapped key is undefined for type ${JSON.stringify( t.args[0], )}`, ); } else { return ValueOrErrors.Default.return(possiblyUndefinedKey); } }) .Then((key) => dispatchToAPIRawValue( t.args[1], types, converters, injectedPrimitives, )( (keyValue as ValueTuple).values.get(1)!, formState?.elementFormStates?.get(index)?.ValueFormState, ).Then((value) => ValueOrErrors.Default.return([key, value] as [any, any]), ), ); }); return ValueOrErrors.Operations.All(List(keyValues)).Then((values) => { if ( values.map((kv) => JSON.stringify(kv[0])).toSet().size != values.size ) { return ValueOrErrors.Default.throwOne( "Keys in the map are not unique", ); } return ValueOrErrors.Operations.Return( converters["Map"].toAPIRawValue([ values, formState?.commonFormState?.modifiedByUser ?? false, ]), ); }); } if (t.kind == "sum" && t.args.length === 2) { if (!PredicateValue.Operations.IsSum(raw)) { return ValueOrErrors.Default.throwOne( `Sum expected but got ${JSON.stringify(raw)}`, ); } return dispatchToAPIRawValue( raw.value.kind == "l" ? t.args[0] : t.args[1], types, converters, injectedPrimitives, )( raw.value.value, raw.value.kind == "l" ? formState?.commonFormState?.left : formState?.commonFormState?.right, ).Then((value) => ValueOrErrors.Default.return( converters["Sum"].toAPIRawValue([ raw.value.kind == "l" ? Sum.Default.left(value) : Sum.Default.right(value), formState?.commonFormState?.modifiedByUser ?? false, ]), ), ); } if (t.kind == "sumN") { if (!PredicateValue.Operations.IsSumN(raw)) { return ValueOrErrors.Default.throwOne( `SumN expected but got ${JSON.stringify(raw)}`, ); } return dispatchToAPIRawValue( t.args[raw.caseIndex], types, converters, injectedPrimitives, )(raw.value, formState?.commonFormState?.modifiedByUser).Then((value) => ValueOrErrors.Default.return( converters["SumN"].toAPIRawValue([ PredicateValue.Default.sumN(raw.caseIndex, raw.arity, value), formState?.commonFormState?.modifiedByUser ?? false, ]), ), ); } if (t.kind == "tuple") { if (!PredicateValue.Operations.IsTuple(raw)) { return ValueOrErrors.Default.throwOne( `Tuple expected but got ${JSON.stringify(raw)}`, ); } return ValueOrErrors.Operations.All( List( raw.values.map((value, index) => { return dispatchToAPIRawValue( t.args[index], types, converters, injectedPrimitives, )(value, formState?.itemFormStates?.get(index)); }), ), ).Then((values) => ValueOrErrors.Default.return( converters["Tuple"].toAPIRawValue([ values, formState?.commonFormState?.modifiedByUser ?? false, ]), ), ); } if (t.kind == "lookup") return dispatchToAPIRawValue( types.get(t.name)!, types, converters, injectedPrimitives, )(raw, formState); // TODO -- this can be more functional if (t.kind == "record") { if (!PredicateValue.Operations.IsRecord(raw)) { return ValueOrErrors.Default.throwOne( `Record expected but got ${JSON.stringify(raw)}`, ); } const res = [] as any; t.fields.forEach((fieldType, fieldName) => { const rawField = raw.fields.get(fieldName); if (rawField == undefined) { return; } res.push([ fieldName, dispatchToAPIRawValue( fieldType, types, converters, injectedPrimitives, )( raw.fields.get(fieldName)!, formState?.fieldStates?.get(fieldName), ), ]); }); const errors: ValueOrErrors< List, string > = ValueOrErrors.Operations.All( List( res.map( ([_, value]: [_: string, value: ValueOrErrors]) => value, ), ), ); if (errors.kind == "errors") return errors; const result = res.reduce( (acc: any, [fieldName, value]: [fieldName: string, value: any]) => { acc[fieldName] = value.value; return acc; }, {} as any, ); return ValueOrErrors.Operations.Return( converters["Record"].toAPIRawValue([ result, formState?.commonFormState?.modifiedByUser ?? false, ]), ); } if (t.kind == "one") { if (!PredicateValue.Operations.IsOption(raw)) { return ValueOrErrors.Default.throwOne( `Option expected but got ${JSON.stringify(raw)}\n...when converting one to API raw value`, ); } if (!raw.isSome) { return ValueOrErrors.Default.return( converters["One"].toAPIRawValue([ raw, formState?.commonFormState?.modifiedByUser ?? false, ]), ); } return dispatchToAPIRawValue( t.arg, types, converters, injectedPrimitives, )(raw.value, formState?.commonFormState?.modifiedByUser ?? false).Then( (value) => { return ValueOrErrors.Default.return( converters["One"].toAPIRawValue([ PredicateValue.Default.option(true, value), formState?.commonFormState?.modifiedByUser ?? false, ]), ); }, ); } if (t.kind == "readOnly") { if (!PredicateValue.Operations.IsReadOnly(raw)) { return ValueOrErrors.Default.throwOne( `ReadOnly expected but got ${JSON.stringify(raw)}`, ); } return dispatchToAPIRawValue( t.arg, types, converters, injectedPrimitives, )(raw.ReadOnly, formState).Then((childValue) => ValueOrErrors.Default.return( converters["ReadOnly"].toAPIRawValue([ { ReadOnly: childValue }, formState?.commonFormState?.modifiedByUser ?? false, ]), ), ); } if (t.kind == "table") { if (!PredicateValue.Operations.IsTable(raw)) { return ValueOrErrors.Default.throwOne( `Table expected but got ${JSON.stringify(raw)}`, ); } return ValueOrErrors.Default.return( converters["Table"].toAPIRawValue([ raw, formState?.commonFormState?.modifiedByUser ?? false, ]), ); } // Filters if (t.kind == "contains") { if (!PredicateValue.Operations.IsFilterContains(raw)) { return ValueOrErrors.Default.throwOne( `FilterContains expected but got ${JSON.stringify(raw)}`, ); } return dispatchToAPIRawValue( t.contains, types, converters, injectedPrimitives, )(raw.contains, formState).Then((value) => ValueOrErrors.Default.return( converters["Contains"].toAPIRawValue([ PredicateValue.Default.filterContains(value), formState?.commonFormState?.modifiedByUser ?? false, ]), ), ); } if (t.kind == "=") { if (!PredicateValue.Operations.IsFilterEqualsTo(raw)) { return ValueOrErrors.Default.throwOne( `FilterEqualsTo expected but got ${JSON.stringify(raw)}`, ); } return dispatchToAPIRawValue( t.equalsTo, types, converters, injectedPrimitives, )(raw.equalsTo, formState).Then((value) => ValueOrErrors.Default.return( converters["="].toAPIRawValue([ PredicateValue.Default.filterEqualsTo(value), formState?.commonFormState?.modifiedByUser ?? false, ]), ), ); } if (t.kind == "!=") { if (!PredicateValue.Operations.IsFilterNotEqualsTo(raw)) { return ValueOrErrors.Default.throwOne( `FilterNotEqualsTo expected but got ${JSON.stringify(raw)}`, ); } return dispatchToAPIRawValue( t.notEqualsTo, types, converters, injectedPrimitives, )(raw.notEqualsTo, formState).Then((value) => ValueOrErrors.Default.return( converters["!="].toAPIRawValue([ PredicateValue.Default.filterNotEqualsTo(value), formState?.commonFormState?.modifiedByUser ?? false, ]), ), ); } if (t.kind == ">=") { if (!PredicateValue.Operations.IsFilterGreaterThanOrEqualsTo(raw)) { return ValueOrErrors.Default.throwOne( `FilterGreaterThanOrEqualsTo expected but got ${JSON.stringify(raw)}`, ); } return dispatchToAPIRawValue( t.greaterThanOrEqualsTo, types, converters, injectedPrimitives, )(raw.greaterThanOrEqualsTo, formState).Then((value) => ValueOrErrors.Default.return( converters[">="].toAPIRawValue([ PredicateValue.Default.filterGreaterThanOrEqualsTo(value), formState?.commonFormState?.modifiedByUser ?? false, ]), ), ); } if (t.kind == ">") { if (!PredicateValue.Operations.IsFilterGreaterThan(raw)) { return ValueOrErrors.Default.throwOne( `FilterGreaterThan expected but got ${JSON.stringify(raw)}`, ); } return dispatchToAPIRawValue( t.greaterThan, types, converters, injectedPrimitives, )(raw.greaterThan, formState).Then((value) => ValueOrErrors.Default.return( converters[">"].toAPIRawValue([ PredicateValue.Default.filterGreaterThan(value), formState?.commonFormState?.modifiedByUser ?? false, ]), ), ); } if (t.kind == "!=null") { if (!PredicateValue.Operations.IsFilterIsNotNull(raw)) { return ValueOrErrors.Default.throwOne( `FilterIsNotNull expected but got ${JSON.stringify(raw)}`, ); } return ValueOrErrors.Default.return( converters["!=null"].toAPIRawValue([ raw, formState?.commonFormState?.modifiedByUser ?? false, ]), ); } if (t.kind == "=null") { if (!PredicateValue.Operations.IsFilterIsNull(raw)) { return ValueOrErrors.Default.throwOne( `FilterIsNull expected but got ${JSON.stringify(raw)}`, ); } return ValueOrErrors.Default.return( converters["=null"].toAPIRawValue([ raw, formState?.commonFormState?.modifiedByUser ?? false, ]), ); } if (t.kind == "<=") { if (!PredicateValue.Operations.IsFilterSmallerThanOrEqualsTo(raw)) { return ValueOrErrors.Default.throwOne( `FilterSmallerThanOrEqualsTo expected but got ${JSON.stringify(raw)}`, ); } return dispatchToAPIRawValue( t.smallerThanOrEqualsTo, types, converters, injectedPrimitives, )(raw.smallerThanOrEqualsTo, formState).Then((value) => ValueOrErrors.Default.return( converters["<="].toAPIRawValue([ PredicateValue.Default.filterSmallerThanOrEqualsTo(value), formState?.commonFormState?.modifiedByUser ?? false, ]), ), ); } if (t.kind == "<") { if (!PredicateValue.Operations.IsFilterSmallerThan(raw)) { return ValueOrErrors.Default.throwOne( `FilterSmallerThan expected but got ${JSON.stringify(raw)}`, ); } return dispatchToAPIRawValue( t.smallerThan, types, converters, injectedPrimitives, )(raw.smallerThan, formState).Then((value) => ValueOrErrors.Default.return( converters["<"].toAPIRawValue([ PredicateValue.Default.filterSmallerThan(value), formState?.commonFormState?.modifiedByUser ?? false, ]), ), ); } if (t.kind == "startsWith") { if (!PredicateValue.Operations.IsFilterStartsWith(raw)) { return ValueOrErrors.Default.throwOne( `FilterStartsWith expected but got ${JSON.stringify(raw)}`, ); } return dispatchToAPIRawValue( t.startsWith, types, converters, injectedPrimitives, )(raw.startsWith, formState).Then((value) => ValueOrErrors.Default.return( converters["StartsWith"].toAPIRawValue([ PredicateValue.Default.filterStartsWith(value), formState?.commonFormState?.modifiedByUser ?? false, ]), ), ); } return ValueOrErrors.Default.throwOne( `Unsupported type ${JSON.stringify(t)}`, ); })(); return result.MapErrors((errors) => errors.map( (error) => `${error}\n...When converting type ${JSON.stringify( t, null, 2, )} and value ${JSON.stringify(raw, null, 2)} to API raw value`, ), ); };