From c5c77d5bda96ab834dbd95f2744ed3fb8961d9aa Mon Sep 17 00:00:00 2001 From: bduran Date: Wed, 30 Oct 2024 15:35:13 -0700 Subject: [PATCH] add file input to constraint form --- .../constraints/ConstraintForm.svelte | 31 +++++++++- .../constraints/ConstraintsPanel.svelte | 11 +--- .../goals/SchedulingGoalForm.svelte | 2 +- .../ui/Association/AssociationForm.svelte | 5 +- src/routes/constraints/edit/[id]/+page.svelte | 59 ++++++++++++------- src/types/constraint.ts | 12 ++-- src/utilities/effects.ts | 35 +++++++++-- 7 files changed, 112 insertions(+), 43 deletions(-) diff --git a/src/components/constraints/ConstraintForm.svelte b/src/components/constraints/ConstraintForm.svelte index 90a6064bb4..884738e06c 100644 --- a/src/components/constraints/ConstraintForm.svelte +++ b/src/components/constraints/ConstraintForm.svelte @@ -4,7 +4,8 @@ import { goto } from '$app/navigation'; import { base } from '$app/paths'; import { createEventDispatcher } from 'svelte'; - import type { DefinitionType } from '../../enums/association'; + import { DefinitionType } from '../../enums/association'; + import { ConstraintDefinitionType } from '../../enums/constraint'; import { SearchParameters } from '../../enums/searchParameters'; import { constraints } from '../../stores/constraints'; import type { User, UserId } from '../../types/app'; @@ -17,11 +18,13 @@ export let initialConstraintDefinitionAuthor: UserId | undefined = undefined; export let initialConstraintDefinitionCode: string | null = ''; + export let initialConstraintDefinitionFilename: string | null = null; export let initialConstraintDescription: string = ''; export let initialConstraintId: number | null = null; export let initialConstraintName: string = ''; export let initialConstraintPublic: boolean = true; export let initialConstraintDefinitionTags: Tag[] = []; + export let initialConstraintDefinitionType: ConstraintDefinitionType = ConstraintDefinitionType.EDSL; export let initialConstraintMetadataTags: Tag[] = []; export let initialConstraintOwner: UserId = null; export let initialConstraintRevision: number | null = null; @@ -100,13 +103,24 @@ }>, ) { const { - detail: { definitionCode, definitionTags, description, name, public: isPublic, tags: metadataTags }, + detail: { + definitionCode, + definitionFile, + definitionTags, + definitionType, + description, + name, + public: isPublic, + tags: metadataTags, + }, } = event; const newConstraintId = await effects.createConstraint( name, isPublic, metadataTags.map(({ id }) => ({ tag_id: id })), + definitionType === DefinitionType.CODE ? ConstraintDefinitionType.EDSL : ConstraintDefinitionType.JAR, definitionCode ?? '', + definitionFile ?? null, definitionTags.map(({ id }) => ({ tag_id: id })), user, description, @@ -130,12 +144,14 @@ }>, ) { const { - detail: { definitionCode, definitionTags }, + detail: { definitionCode, definitionFile, definitionTags, definitionType }, } = event; if (initialConstraintId !== null) { const definition = await effects.createConstraintDefinition( initialConstraintId, + definitionType === DefinitionType.CODE ? ConstraintDefinitionType.EDSL : ConstraintDefinitionType.JAR, definitionCode ?? '', + definitionFile ?? null, definitionTags.map(({ id }) => ({ tag_id: id })), user, ); @@ -228,12 +244,20 @@ {\n\n}\n`} + definitionTypeConfigurations={{ + code: { label: 'EDSL' }, + file: { accept: '.jar', label: 'JAR File' }, + }} displayName="Constraint" {hasCreateDefinitionCodePermission} {hasWriteMetadataPermission} {hasWriteDefinitionTagsPermission} initialDefinitionAuthor={initialConstraintDefinitionAuthor} + initialDefinitionType={initialConstraintDefinitionType === ConstraintDefinitionType.EDSL + ? DefinitionType.CODE + : DefinitionType.FILE} initialDefinitionCode={initialConstraintDefinitionCode} + initialDefinitionFileName={initialConstraintDefinitionFilename} initialDescription={initialConstraintDescription} initialId={initialConstraintId} initialName={initialConstraintName} @@ -245,6 +269,7 @@ {initialReferenceModelId} {permissionError} revisions={constraintRevisions} + showDefinitionTypeSelector={true} {tags} tsFiles={constraintsTsFiles} {mode} diff --git a/src/components/constraints/ConstraintsPanel.svelte b/src/components/constraints/ConstraintsPanel.svelte index 28f1b29eae..34b9825d96 100644 --- a/src/components/constraints/ConstraintsPanel.svelte +++ b/src/components/constraints/ConstraintsPanel.svelte @@ -153,7 +153,7 @@ async function onDuplicateConstraintInvocation(event: CustomEvent) { const { - detail: { constraint_metadata, constraint_invocation_id, ...constraintPlanSpec }, + detail: { constraint_metadata, invocation_id, ...constraintPlanSpec }, } = event; if ($plan) { await effects.createConstraintPlanSpecification(constraintPlanSpec, user); @@ -165,12 +165,7 @@ detail: { constraint_metadata, specification_id, ...constraintPlanSpec }, } = event; if ($plan) { - await effects.deleteConstraintInvocations( - $plan, - specification_id, - [constraintPlanSpec.constraint_invocation_id], - user, - ); + await effects.deleteConstraintInvocations($plan, specification_id, [constraintPlanSpec.invocation_id], user); } } @@ -376,7 +371,7 @@ - {#each filteredConstraints as constraint (constraint.constraint_invocation_id)} + {#each filteredConstraints as constraint (constraint.invocation_id)} {#if $constraintsMap[constraint.constraint_id]} 0); $: isDefinitionTagsModified = diffTags(initialDefinitionTags || [], definitionTags); $: hasUpdateDefinitionPermission = hasWriteDefinitionTagsPermission || isDefinitionModified; $: pageTitle = mode === 'edit' ? 's' : 'New '; @@ -168,6 +168,9 @@ owner !== '' && definitionCode !== '' && name !== '' && + (mode === 'create' && definitionType === DefinitionType.FILE + ? !initialDefinitionFileName && (definitionFiles?.length ?? 0) > 0 + : true) && (isMetadataModified || (isDefinitionTagsModified && hasUpdateDefinitionPermission) || isDefinitionModified); $: saveButtonClass = saveButtonEnabled ? 'primary' : 'secondary'; $: if (mode === 'edit' && (isMetadataModified || isDefinitionModified)) { diff --git a/src/routes/constraints/edit/[id]/+page.svelte b/src/routes/constraints/edit/[id]/+page.svelte index 61d5fe47e1..429d13c3cd 100644 --- a/src/routes/constraints/edit/[id]/+page.svelte +++ b/src/routes/constraints/edit/[id]/+page.svelte @@ -9,6 +9,7 @@ import { constraintMetadata, constraintMetadataId } from '../../../../stores/constraints'; import { tags } from '../../../../stores/tags'; import type { ConstraintDefinition, ConstraintMetadataVersionDefinition } from '../../../../types/constraint'; + import effects from '../../../../utilities/effects'; import { getSearchParameterNumber, setQueryParam } from '../../../../utilities/generic'; import type { PageData } from './$types'; @@ -18,12 +19,16 @@ getSearchParameterNumber(SearchParameters.REVISION, $page.url.searchParams) ?? data.initialConstraint.versions[0].revision; - let constraintDefinition: Pick = - data.initialConstraint.versions.find( - ({ revision }) => revision === constraintRevision, - ) as ConstraintMetadataVersionDefinition; + let constraintDefinition: Pick< + ConstraintDefinition, + 'author' | 'definition' | 'revision' | 'tags' | 'type' | 'uploaded_jar_id' + > = data.initialConstraint.versions.find( + ({ revision }) => revision === constraintRevision, + ) as ConstraintMetadataVersionDefinition; let constraintDefinitionCode = constraintDefinition?.definition; let constraintDefinitionAuthor = constraintDefinition?.author; + let constraintDefinitionFilename: string | null = null; + let constraintDefinitionType = constraintDefinition.type; let constraintDescription = data.initialConstraint.description; let constraintId = data.initialConstraint.id; let constraintName = data.initialConstraint.name; @@ -35,24 +40,32 @@ let referenceModelId: number | null = null; $: $constraintMetadataId = data.initialConstraint.id; - $: if ($constraintMetadata != null && $constraintMetadata.id === $constraintMetadataId) { - constraintDefinition = $constraintMetadata.versions.find( - ({ revision }) => revision === constraintRevision, - ) as ConstraintMetadataVersionDefinition; - if (constraintDefinition != null) { - constraintDefinitionAuthor = constraintDefinition?.author; - constraintDefinitionCode = constraintDefinition?.definition; - constraintDefinitionTags = constraintDefinition?.tags.map(({ tag }) => tag); - } + $: (async () => { + if ($constraintMetadata != null && $constraintMetadata.id === $constraintMetadataId) { + constraintDefinition = $constraintMetadata.versions.find( + ({ revision }) => revision === constraintRevision, + ) as ConstraintMetadataVersionDefinition; + if (constraintDefinition != null) { + constraintDefinitionAuthor = constraintDefinition?.author; + constraintDefinitionType = constraintDefinition?.type; + constraintDefinitionCode = constraintDefinition?.definition; + constraintDefinitionTags = constraintDefinition?.tags.map(({ tag }) => tag); + if (constraintDefinition.uploaded_jar_id !== null) { + constraintDefinitionFilename = await effects.getFileName(constraintDefinition.uploaded_jar_id, data.user); + } else { + constraintDefinitionFilename = null; + } + } - constraintDescription = $constraintMetadata.description; - constraintId = $constraintMetadata.id; - constraintName = $constraintMetadata.name; - constraintPublic = $constraintMetadata.public; - constraintMetadataTags = $constraintMetadata.tags.map(({ tag }) => tag); - constraintOwner = $constraintMetadata.owner; - constraintRevisions = $constraintMetadata.versions.map(({ revision }) => revision); - } + constraintDescription = $constraintMetadata.description; + constraintId = $constraintMetadata.id; + constraintName = $constraintMetadata.name; + constraintPublic = $constraintMetadata.public; + constraintMetadataTags = $constraintMetadata.tags.map(({ tag }) => tag); + constraintOwner = $constraintMetadata.owner; + constraintRevisions = $constraintMetadata.versions.map(({ revision }) => revision); + } + })(); function onRevisionSelect(event: CustomEvent) { const { detail: revision } = event; @@ -79,8 +92,10 @@ ; export type ConstraintMetadata = BaseMetadata; @@ -32,7 +33,6 @@ export type ConstraintModelSpecification = { export type ConstraintPlanSpecification = { arguments: any; constraint_id: number; - constraint_invocation_id?: number; constraint_metadata: | (Pick & { versions: Pick[]; @@ -40,6 +40,7 @@ export type ConstraintPlanSpecification = { | null; constraint_revision: number | null; enabled: boolean; + invocation_id?: number; plan_id: number; specification_id: number; // constraint_definition: ConstraintDefinition; @@ -48,9 +49,12 @@ export type ConstraintPlanSpecification = { export type ConstraintModelSpecInsertInput = Omit; export type ConstraintPlanSpecSetInput = Omit; -export type ConstraintPlanSpecInsertInput = Omit; +export type ConstraintPlanSpecInsertInput = Omit; -export type ConstraintDefinitionInsertInput = Pick & { +export type ConstraintDefinitionInsertInput = Pick< + ConstraintDefinition, + 'constraint_id' | 'definition' | 'type' | 'uploaded_jar_id' +> & { tags: { data: ConstraintTagsInsertInput[]; }; diff --git a/src/utilities/effects.ts b/src/utilities/effects.ts index 5be4728767..43d55874da 100644 --- a/src/utilities/effects.ts +++ b/src/utilities/effects.ts @@ -7,6 +7,7 @@ import { type ParameterDictionary as AmpcsParameterDictionary, } from '@nasa-jpl/aerie-ampcs'; import { get } from 'svelte/store'; +import { ConstraintDefinitionType } from '../enums/constraint'; import { DictionaryTypes } from '../enums/dictionaryTypes'; import { SchedulingDefinitionType } from '../enums/scheduling'; import { SearchParameters } from '../enums/searchParameters'; @@ -604,7 +605,9 @@ const effects = { name: string, isPublic: boolean, metadataTags: ConstraintTagsInsertInput[], + definitionType: ConstraintDefinitionType, definition: string, + file: File | null, definitionTags: ConstraintTagsInsertInput[], user: User | null, description?: string, @@ -614,6 +617,15 @@ const effects = { throwPermissionError('create a constraint'); } + let jarId: number | null = null; + let codeDefinition: string | null = null; + + if (definitionType === ConstraintDefinitionType.EDSL) { + codeDefinition = definition; + } else if (definitionType === ConstraintDefinitionType.JAR && file) { + jarId = await effects.uploadFile(file, user); + } + const constraintInsertInput: ConstraintInsertInput = { ...(description ? { description } : {}), name, @@ -624,10 +636,12 @@ const effects = { versions: { data: [ { - definition, + definition: codeDefinition, tags: { data: definitionTags, }, + type: definitionType, + uploaded_jar_id: jarId, }, ], }, @@ -655,7 +669,9 @@ const effects = { async createConstraintDefinition( constraintId: number, + definitionType: ConstraintDefinitionType, definition: string, + file: File | null, definitionTags: ConstraintTagsInsertInput[], user: User | null, ): Promise | null> { @@ -664,12 +680,23 @@ const effects = { throwPermissionError('create a constraint'); } + let jarId: number | null = null; + let codeDefinition: string | null = null; + + if (definitionType === ConstraintDefinitionType.EDSL) { + codeDefinition = definition; + } else if (definitionType === ConstraintDefinitionType.JAR && file !== null) { + jarId = await effects.uploadFile(file, user); + } + const constraintDefinitionInsertInput: ConstraintDefinitionInsertInput = { constraint_id: constraintId, - definition, + definition: codeDefinition, tags: { data: definitionTags, }, + type: definitionType, + uploaded_jar_id: jarId, }; const data = await reqHasura( gql.CREATE_CONSTRAINT_DEFINITION, @@ -5665,7 +5692,7 @@ const effects = { const { enabled, constraint_id: constraintId, - constraint_invocation_id, + invocation_id, constraint_revision: revision, } = constraintPlanSpecification; @@ -5673,9 +5700,9 @@ const effects = { gql.UPDATE_CONSTRAINT_PLAN_SPECIFICATION, { arguments: constraintPlanSpecification.arguments, - constraint_invocation_id, enabled, id: constraintId, + invocation_id, planId: plan.id, revision, },