import { List, Map } from "immutable"; import { DispatchParsedType, DispatchTypeName, SumNType, } from "./domains/specification/domains/types/state"; import { unit, Unit } from "../../../../fun/domains/unit/state"; import { PredicateValue, EnumReference, ValueOrErrors, DispatchInjectedPrimitives, BasicFun, Guid, ApiErrors, Specification, Synchronized, simpleUpdater, ValueInfiniteStreamState, MapRepo, DispatchInjectables, RecordAbstractRendererView, DispatchInjectablesTypes, SpecificationApis, LookupTypeAbstractRendererView, ValueFilter, ValueTable, } from "../../../../../main"; import { DispatchApiConverters, concreteRendererToKind, dispatchDefaultState, dispatchDefaultValue, dispatchFromAPIRawValue, dispatchToAPIRawValue, tryGetConcreteRenderer, ConcreteRenderers, getDefaultRecordRenderer, } from "../built-ins/state"; import { SearchableInfiniteStreamAbstractRendererState } from "../runner/domains/abstract-renderers/searchable-infinite-stream/state"; import { Renderer } from "./domains/specification/domains/forms/domains/renderer/state"; export type DispatchParsedPassthroughLauncher = { kind: "passthrough"; formName: string; renderer: Renderer; parseEntityFromApi: (_: any) => ValueOrErrors; parseGlobalConfigurationFromApi: ( _: any, ) => ValueOrErrors; parseValueToApi: ( value: PredicateValue, type: DispatchParsedType, state: any, ) => ValueOrErrors; type: DispatchParsedType; }; export type DispatchParsedEditLauncher = { kind: "edit"; formName: string; renderer: Renderer; fromApiParser: (_: any) => ValueOrErrors; toApiParser: ( value: PredicateValue, type: DispatchParsedType, formState: any, ) => ValueOrErrors; parseGlobalConfigurationFromApi: ( _: any, ) => ValueOrErrors; api: string; configApi: string; type: DispatchParsedType; }; export type DispatchParsedCreateLauncher = { kind: "create"; formName: string; renderer: Renderer; fromApiParser: (_: any) => ValueOrErrors; toApiParser: ( value: PredicateValue, type: DispatchParsedType, formState: any, ) => ValueOrErrors; parseGlobalConfigurationFromApi: ( _: any, ) => ValueOrErrors; api: string; configApi: string; type: DispatchParsedType; }; export type DispatchParsedLauncher = | DispatchParsedPassthroughLauncher | DispatchParsedEditLauncher | DispatchParsedCreateLauncher; export type DispatchParsedLaunchers = { passthrough: Map>; edit: Map>; create: Map>; }; export type IdWrapperProps = { domNodeId: string; children: React.ReactNode; }; export type ErrorRendererProps = { message: string; }; export type DispatcherContext< T extends DispatchInjectablesTypes, Flags, CustomPresentationContext, ExtraContext, > = { injectedPrimitives: DispatchInjectedPrimitives | undefined; apiConverters: DispatchApiConverters; specApis: SpecificationApis; getConcreteRendererKind: (viewName: string) => ValueOrErrors; getConcreteRenderer: ReturnType< typeof tryGetConcreteRenderer< T, Flags, CustomPresentationContext, ExtraContext > >; lookupTypeRenderer: () => LookupTypeAbstractRendererView< CustomPresentationContext, Flags, ExtraContext >; getDefaultRecordRenderer: ( isNested: boolean, ) => RecordAbstractRendererView< CustomPresentationContext, Flags, ExtraContext >; concreteRenderers: ConcreteRenderers< T, Flags, CustomPresentationContext, ExtraContext >; defaultValue: ( t: DispatchParsedType, renderer: Renderer, ) => ValueOrErrors; defaultState: ( infiniteStreamSources: DispatchInfiniteStreamSources, lookupSources: DispatchLookupSources | undefined, tableApiSources: DispatchTableApiSources | undefined, ) => ( t: DispatchParsedType, renderer: Renderer, ) => ValueOrErrors; forms: Map>; types: Map>; IdProvider: (props: IdWrapperProps) => React.ReactNode; ErrorRenderer: (props: ErrorRendererProps) => React.ReactNode; parseFromApiByType: ( type: DispatchParsedType, ) => (raw: any) => ValueOrErrors; parseToApiByType: ( type: DispatchParsedType, value: PredicateValue, state: any, ) => ValueOrErrors; }; export type DeserializedDispatchSpecification< T extends DispatchInjectablesTypes, Flags = Unit, CustomPresentationContext = Unit, ExtraContext = Unit, > = { launchers: DispatchParsedLaunchers; dispatcherContext: DispatcherContext< T, Flags, CustomPresentationContext, ExtraContext >; parseValueToApi: ( value: PredicateValue, type: DispatchParsedType, state: any, ) => ValueOrErrors; parseEntityFromApiByTypeLookupName: ( typeLookupName: string, _: any, ) => ValueOrErrors; getTypeByLookupName: ( typeLookupName: string, ) => ValueOrErrors, string>; }; export type DispatchSpecificationDeserializationResult< T extends DispatchInjectablesTypes, Flags = Unit, CustomPresentationContext = Unit, ExtraContext = Unit, > = ValueOrErrors< DeserializedDispatchSpecification< T, Flags, CustomPresentationContext, ExtraContext >, string >; export type DispatchEnumName = string; export type DispatchEnumOptionsSources = BasicFun< DispatchEnumName, ValueOrErrors>>, string> >; export type DispatchStreamName = string; export type DispatchInfiniteStreamSources = BasicFun< DispatchStreamName, ValueOrErrors< SearchableInfiniteStreamAbstractRendererState["customFormState"]["getChunk"], string > >; export type DispatchTableFiltersAndSorting = { filters: Map>; sorting: Map; }; export type TableGetManyParams = { chunkSize: number; from: number; filtersAndSorting: string; parentId?: string; }; export type DispatchTableApiName = string; export type DispatchTableApiSource = { get: BasicFun>; getMany: BasicFun< BasicFun>, BasicFun> >; getDefaultFiltersAndSorting: BasicFun< Map>, BasicFun< BasicFun< DispatchParsedType, BasicFun> >, BasicFun> > >; }; export type DispatchTableApiSources = BasicFun< string, ValueOrErrors >; export type DispatchApiName = string; export type DispatchOneSource = { get: BasicFun> | undefined; getManyUnlinked: | BasicFun< BasicFun>, BasicFun< Guid, BasicFun, ValueInfiniteStreamState["getChunk"]> > > | undefined; }; export type DispatchLookupSources = (typeName: string) => ValueOrErrors< { one?: BasicFun>; }, string >; export type DispatchConfigName = string; export type DispatchGlobalConfigurationSources = BasicFun< DispatchConfigName, Promise >; export type DispatchEntityName = string; export type DispatchEntityApis = { create: BasicFun>>; default: BasicFun>>; update: BasicFun< DispatchEntityName, (id: Guid, entity: any) => Promise >; get: BasicFun>>; }; export const parseDispatchFormsToLaunchers = < T extends DispatchInjectablesTypes, Flags, CustomPresentationContext, ExtraContext, >( injectedPrimitives: DispatchInjectedPrimitives | undefined, apiConverters: DispatchApiConverters, lookupTypeRenderer: () => LookupTypeAbstractRendererView< CustomPresentationContext, Flags, ExtraContext >, defaultRecordRenderer: () => RecordAbstractRendererView< CustomPresentationContext, Flags, ExtraContext >, defaultNestedRecordRenderer: () => RecordAbstractRendererView< CustomPresentationContext, Flags, ExtraContext >, concreteRenderers: ConcreteRenderers< T, Flags, CustomPresentationContext, ExtraContext >, IdProvider: (props: IdWrapperProps) => React.ReactNode, ErrorRenderer: (props: ErrorRendererProps) => React.ReactNode, ) => ( specification: Specification, ): DispatchSpecificationDeserializationResult< T, Flags, CustomPresentationContext, ExtraContext > => ValueOrErrors.Operations.All( List< ValueOrErrors<[string, DispatchParsedPassthroughLauncher], string> >( specification.launchers.passthrough .entrySeq() .toArray() .map(([launcherName, launcher]) => MapRepo.Operations.tryFindWithError( launcher.form, specification.forms, () => `cannot find form "${launcher.form}" when parsing launchers`, ).Then((parsedForm) => MapRepo.Operations.tryFindWithError( launcher.configType, specification.types, () => `cannot find global config type "${launcher.configType}" when parsing launchers`, ).Then((globalConfigType) => ValueOrErrors.Default.return([ launcherName, { kind: "passthrough", renderer: parsedForm, type: parsedForm.type, parseEntityFromApi: (raw: any) => dispatchFromAPIRawValue( parsedForm.type, specification.types, apiConverters, injectedPrimitives, )(raw), parseGlobalConfigurationFromApi: (raw: any) => dispatchFromAPIRawValue( globalConfigType, specification.types, apiConverters, injectedPrimitives, )(raw), parseValueToApi: ( value: PredicateValue, type: DispatchParsedType, state: any, ) => dispatchToAPIRawValue( type, specification.types, apiConverters, injectedPrimitives, )(value, state), formName: launcher.form, }, ]), ), ), ), ), ) .MapErrors((errors) => errors.map( (error) => `${error}\n...When parsing passthrough launchers`, ), ) .Then((passthroughLaunchers) => ValueOrErrors.Operations.All( List< ValueOrErrors<[string, DispatchParsedCreateLauncher], string> >( specification.launchers.create .entrySeq() .toArray() .map(([launcherName, launcher]) => MapRepo.Operations.tryFindWithError( launcher.form, specification.forms, () => `cannot find form "${launcher.form}" when parsing launchers`, ).Then((parsedForm) => MapRepo.Operations.tryFindWithError( launcher.configApi, specification.apis.entities, () => `cannot find global config api "${launcher.configApi}" when parsing launchers`, ).Then((globalConfigApi) => MapRepo.Operations.tryFindWithError( globalConfigApi.type, specification.types, () => `cannot find global config type "${globalConfigApi.type}" when parsing launchers`, ).Then((globalConfigType) => ValueOrErrors.Default.return([ launcherName, { kind: "create", renderer: parsedForm, type: parsedForm.type, fromApiParser: (raw: any) => dispatchFromAPIRawValue( parsedForm.type, specification.types, apiConverters, injectedPrimitives, )(raw), parseGlobalConfigurationFromApi: (raw: any) => dispatchFromAPIRawValue( globalConfigType, specification.types, apiConverters, injectedPrimitives, )(raw), toApiParser: ( value: PredicateValue, type: DispatchParsedType, formState: any, ) => dispatchToAPIRawValue( type, specification.types, apiConverters, injectedPrimitives, )(value, formState), formName: launcher.form, api: launcher.api, configApi: launcher.configApi, }, ]), ), ), ), ), ), ) .MapErrors((errors) => errors.map((error) => `${error}\n...When parsing create launchers`), ) .Then((createLaunchers) => ValueOrErrors.Operations.All( List< ValueOrErrors<[string, DispatchParsedEditLauncher], string> >( specification.launchers.edit .entrySeq() .toArray() .map(([launcherName, launcher]) => MapRepo.Operations.tryFindWithError( launcher.form, specification.forms, () => `cannot find form "${launcher.form}" when parsing launchers`, ).Then((parsedForm) => MapRepo.Operations.tryFindWithError( launcher.configApi, specification.apis.entities, () => `cannot find global config api "${launcher.configApi}" when parsing launchers`, ).Then((globalConfigApi) => MapRepo.Operations.tryFindWithError( globalConfigApi.type, specification.types, () => `cannot find global config type "${globalConfigApi.type}" when parsing launchers`, ).Then((globalConfigType) => ValueOrErrors.Default.return([ launcherName, { kind: "edit", renderer: parsedForm, type: parsedForm.type, fromApiParser: (raw: any) => dispatchFromAPIRawValue( parsedForm.type, specification.types, apiConverters, injectedPrimitives, )(raw), parseGlobalConfigurationFromApi: (raw: any) => dispatchFromAPIRawValue( globalConfigType, specification.types, apiConverters, injectedPrimitives, )(raw), toApiParser: ( value: PredicateValue, type: DispatchParsedType, formState: any, ) => dispatchToAPIRawValue( type, specification.types, apiConverters, injectedPrimitives, )(value, formState), formName: launcher.form, api: launcher.api, configApi: launcher.configApi, }, ]), ), ), ), ), ), ) .MapErrors((errors) => errors.map( (error) => `${error}\n...When parsing edit launchers`, ), ) .Then((editLaunchers) => ValueOrErrors.Default.return({ launchers: { passthrough: Map(passthroughLaunchers), edit: Map(editLaunchers), create: Map(createLaunchers), }, dispatcherContext: { specApis: specification.apis, forms: specification.forms, injectedPrimitives, apiConverters, concreteRenderers, lookupTypeRenderer, getConcreteRendererKind: concreteRendererToKind(concreteRenderers), getConcreteRenderer: tryGetConcreteRenderer(concreteRenderers), getDefaultRecordRenderer: (isNested: boolean) => getDefaultRecordRenderer( isNested, defaultRecordRenderer, defaultNestedRecordRenderer, ), defaultValue: dispatchDefaultValue( injectedPrimitives, specification.types, specification.forms, ), defaultState: ( infiniteStreamSources: DispatchInfiniteStreamSources, lookupSources: DispatchLookupSources | undefined, tableApiSources: DispatchTableApiSources | undefined, ) => dispatchDefaultState( infiniteStreamSources, injectedPrimitives, specification.types, specification.forms, apiConverters, lookupSources, tableApiSources, specification.apis, ), types: specification.types, parseFromApiByType: (type: DispatchParsedType) => dispatchFromAPIRawValue( type, specification.types, apiConverters, injectedPrimitives, ), IdProvider, ErrorRenderer, parseToApiByType: ( type: DispatchParsedType, value: PredicateValue, state: any, ) => dispatchToAPIRawValue( type, specification.types, apiConverters, injectedPrimitives, )(value, state), }, parseEntityFromApiByTypeLookupName: ( typeLookupName: string, raw: any, ) => MapRepo.Operations.tryFindWithError( typeLookupName, specification.types, () => `cannot find type "${typeLookupName}" when parsing launchers`, ).Then((type) => dispatchFromAPIRawValue( type, specification.types, apiConverters, injectedPrimitives, )(raw), ), parseValueToApi: ( value: PredicateValue, type: DispatchParsedType, state: any, ) => dispatchToAPIRawValue( type, specification.types, apiConverters, injectedPrimitives, )(value, state), getTypeByLookupName: (typeLookupName: string) => MapRepo.Operations.tryFindWithError( typeLookupName, specification.types, () => `cannot find type "${typeLookupName}" in types`, ), }), ), ), ) .MapErrors((errors) => errors.map((error) => `${error}\n...When parsing launchers`), ); export type DispatchFormsParserContext< T extends DispatchInjectablesTypes, Flags = Unit, CustomPresentationContext = Unit, ExtraContext = Unit, > = { lookupTypeRenderer: () => LookupTypeAbstractRendererView< CustomPresentationContext, Flags, ExtraContext >; defaultRecordConcreteRenderer: () => RecordAbstractRendererView< CustomPresentationContext, Flags, ExtraContext >; defaultNestedRecordConcreteRenderer: () => RecordAbstractRendererView< CustomPresentationContext, Flags, ExtraContext >; concreteRenderers: ConcreteRenderers< T, Flags, CustomPresentationContext, ExtraContext >; IdWrapper: (props: IdWrapperProps) => React.ReactNode; ErrorRenderer: (props: ErrorRendererProps) => React.ReactNode; fieldTypeConverters: DispatchApiConverters; getFormsConfig: BasicFun>; injectedPrimitives?: DispatchInjectables; desiredLaunchers?: string[]; explicitLookupTypes?: string[]; // types that are always parsed even if not referenced by a launcher }; export type DispatchFormsParserState< T extends DispatchInjectablesTypes, Flags, CustomPresentationContext, ExtraContext, > = { deserializedSpecification: Synchronized< Unit, DispatchSpecificationDeserializationResult< T, Flags, CustomPresentationContext, ExtraContext > >; }; export const DispatchFormsParserState = < T extends DispatchInjectablesTypes, Flags = Unit, CustomPresentationContext = Unit, ExtraContext = Unit, >() => { return { Default: (): DispatchFormsParserState< T, Flags, CustomPresentationContext, ExtraContext > => ({ deserializedSpecification: Synchronized.Default(unit), }), Updaters: { ...simpleUpdater< DispatchFormsParserState< T, Flags, CustomPresentationContext, ExtraContext > >()("deserializedSpecification"), }, }; };