From a5ce95c8a7684e4dd3a8494989428dbe3653343a Mon Sep 17 00:00:00 2001 From: Randy Schott <1815175+schottra@users.noreply.github.com> Date: Tue, 7 Jul 2020 11:26:00 -0700 Subject: [PATCH 1/5] refactor: adding utility to help determine if an input type is supported --- .../inputHelpers/test/inputHelpers.test.ts | 22 ++------ .../inputHelpers/test/testCases.ts | 18 +++++++ .../inputHelpers/test/utils.test.ts | 51 +++++++++++++++++++ .../LaunchWorkflowForm/inputHelpers/utils.ts | 35 ++++++++++++- 4 files changed, 107 insertions(+), 19 deletions(-) create mode 100644 src/components/Launch/LaunchWorkflowForm/inputHelpers/test/utils.test.ts diff --git a/src/components/Launch/LaunchWorkflowForm/inputHelpers/test/inputHelpers.test.ts b/src/components/Launch/LaunchWorkflowForm/inputHelpers/test/inputHelpers.test.ts index 95ec7e687..6f1a30d14 100644 --- a/src/components/Launch/LaunchWorkflowForm/inputHelpers/test/inputHelpers.test.ts +++ b/src/components/Launch/LaunchWorkflowForm/inputHelpers/test/inputHelpers.test.ts @@ -12,6 +12,8 @@ import { collectionChildToString } from '../utils'; import { literalTestCases, literalToInputTestCases, + supportedPrimitives, + unsupportedTypes, validityTestCases } from './testCases'; @@ -58,14 +60,7 @@ describe('literalToInputValue', () => { }) ); - [ - InputType.Collection, - InputType.Datetime, - InputType.Duration, - InputType.Float, - InputType.Integer, - InputType.String - ].map(type => + supportedPrimitives.map(type => it(`should convert None value for ${type} to undefined`, () => { expect( literalToInputValue({ type }, literalNone()) @@ -176,16 +171,7 @@ describe('inputToLiteral', () => { }); describe('Unsupported Types', () => { - [ - InputType.Binary, - InputType.Blob, - InputType.Error, - InputType.Map, - InputType.None, - InputType.Schema, - InputType.Struct, - InputType.Unknown - ].map(type => + unsupportedTypes.map(type => it(`should return empty value for type: ${type}`, () => { expect( inputToLiteral(makeSimpleInput(type, '')).scalar diff --git a/src/components/Launch/LaunchWorkflowForm/inputHelpers/test/testCases.ts b/src/components/Launch/LaunchWorkflowForm/inputHelpers/test/testCases.ts index 8524c21c3..88180c87f 100644 --- a/src/components/Launch/LaunchWorkflowForm/inputHelpers/test/testCases.ts +++ b/src/components/Launch/LaunchWorkflowForm/inputHelpers/test/testCases.ts @@ -9,6 +9,24 @@ type PrimitiveTestParams = [InputType, any, Core.IPrimitive]; const validDateString = '2019-01-10T00:00:00.000Z'; // Dec 1, 2019 +export const supportedPrimitives = [ + InputType.Boolean, + InputType.Datetime, + InputType.Duration, + InputType.Float, + InputType.Integer +]; + +export const unsupportedTypes = [ + InputType.Binary, + InputType.Blob, + InputType.Error, + InputType.Map, + InputType.None, + InputType.Schema, + InputType.Struct +]; + export const validityTestCases = { boolean: { invalid: ['randomString', {}, new Date()], diff --git a/src/components/Launch/LaunchWorkflowForm/inputHelpers/test/utils.test.ts b/src/components/Launch/LaunchWorkflowForm/inputHelpers/test/utils.test.ts new file mode 100644 index 000000000..6671d9e00 --- /dev/null +++ b/src/components/Launch/LaunchWorkflowForm/inputHelpers/test/utils.test.ts @@ -0,0 +1,51 @@ +import { InputType, InputTypeDefinition } from '../../types'; +import { typeIsSupported } from '../utils'; +import { supportedPrimitives, unsupportedTypes } from './testCases'; + +type TypeIsSupportedTestCase = [string, InputTypeDefinition, boolean]; +describe('Launch/inputHelpers/utils', () => { + describe('typeIsSupported', () => { + const cases: TypeIsSupportedTestCase[] = [ + ...supportedPrimitives.map(type => [ + `supports type ${type}`, + { type }, + true + ]), + ...supportedPrimitives.map(type => [ + `supports 1-dimension collection of type ${type}`, + { type: InputType.Collection, subtype: { type } }, + true + ]), + ...supportedPrimitives.map(type => [ + `supports 2-dimension collection of type: ${type}`, + { + type: InputType.Collection, + subtype: { type: InputType.Collection, subtype: { type } } + }, + true + ]), + ...unsupportedTypes.map(type => [ + `does NOT support type ${type}`, + { type }, + false + ]), + ...unsupportedTypes.map(type => [ + `does NOT support 1-dimension collection of type ${type}`, + { type: InputType.Collection, subtype: { type } }, + false + ]), + ...unsupportedTypes.map(type => [ + `does NOT support 2-dimension collection of type: ${type}`, + { + type: InputType.Collection, + subtype: { type: InputType.Collection, subtype: { type } } + }, + false + ]) + ]; + + cases.forEach(([description, value, expected]) => + it(description, () => expect(typeIsSupported(value)).toBe(expected)) + ); + }); +}); diff --git a/src/components/Launch/LaunchWorkflowForm/inputHelpers/utils.ts b/src/components/Launch/LaunchWorkflowForm/inputHelpers/utils.ts index 5b83a425a..c1695dd99 100644 --- a/src/components/Launch/LaunchWorkflowForm/inputHelpers/utils.ts +++ b/src/components/Launch/LaunchWorkflowForm/inputHelpers/utils.ts @@ -1,6 +1,6 @@ import { Core } from 'flyteidl'; import { get } from 'lodash'; -import { InputType } from '../types'; +import { InputType, InputTypeDefinition } from '../types'; /** Performs a deep get of `path` on the given `Core.ILiteral`. Will throw * if the given property doesn't exist. @@ -25,3 +25,36 @@ export function collectionChildToString(type: InputType, value: any) { } return type === InputType.Integer ? `${value}` : JSON.stringify(value); } + +/** Determines if a given input type, including all levels of nested types, is + * supported for use in the Launch form. + */ +export function typeIsSupported(typeDefinition: InputTypeDefinition): boolean { + const { type, subtype } = typeDefinition; + switch (type) { + case InputType.Map: + case InputType.None: + case InputType.Schema: + case InputType.Unknown: + return false; + case InputType.Boolean: + case InputType.Datetime: + case InputType.Duration: + case InputType.Float: + case InputType.Integer: + case InputType.String: + return true; + case InputType.Collection: { + if (!subtype) { + console.error( + 'Unexpected missing subtype for collection input', + typeDefinition + ); + return false; + } + return typeIsSupported(subtype); + } + default: + return false; + } +} From 4601381838018359252d0a44a02725a993cc93e1 Mon Sep 17 00:00:00 2001 From: Randy Schott <1815175+schottra@users.noreply.github.com> Date: Mon, 13 Jul 2020 17:29:00 -0700 Subject: [PATCH 2/5] fix: adding detection and special rendering for unlaunchable workflows --- .../LaunchWorkflowForm/LaunchWorkflowForm.tsx | 31 +++++--- .../UnsupportedRequiredInputsError.tsx | 32 ++++++++ .../Launch/LaunchWorkflowForm/constants.ts | 4 + .../test/LaunchWorkflowForm.test.tsx | 74 ++++++++++++++++++- .../LaunchWorkflowForm/test/constants.ts | 1 + .../Launch/LaunchWorkflowForm/types.ts | 1 + .../useLaunchWorkflowFormState.ts | 9 +++ .../Launch/LaunchWorkflowForm/utils.ts | 19 ++++- 8 files changed, 157 insertions(+), 14 deletions(-) create mode 100644 src/components/Launch/LaunchWorkflowForm/UnsupportedRequiredInputsError.tsx diff --git a/src/components/Launch/LaunchWorkflowForm/LaunchWorkflowForm.tsx b/src/components/Launch/LaunchWorkflowForm/LaunchWorkflowForm.tsx index 7d48ca917..9157d7983 100644 --- a/src/components/Launch/LaunchWorkflowForm/LaunchWorkflowForm.tsx +++ b/src/components/Launch/LaunchWorkflowForm/LaunchWorkflowForm.tsx @@ -16,12 +16,13 @@ import { workflowSortFields } from 'models'; import * as React from 'react'; -import { formStrings } from './constants'; +import { formStrings, unsupportedRequiredInputsString } from './constants'; import { InputValueCacheContext } from './inputValueCache'; import { LaunchWorkflowFormInputs } from './LaunchWorkflowFormInputs'; import { SearchableSelector } from './SearchableSelector'; import { useStyles } from './styles'; import { LaunchWorkflowFormProps } from './types'; +import { UnsupportedRequiredInputsError } from './UnsupportedRequiredInputsError'; import { useLaunchWorkflowFormState } from './useLaunchWorkflowFormState'; import { workflowsToSearchableSelectorOptions } from './utils'; @@ -67,6 +68,11 @@ export const LaunchWorkflowForm: React.FC = props => { state.onSubmit(); }; + const preventSubmit = + submissionState.loading || + !state.inputLoadingState.hasLoaded || + state.unsupportedRequiredInputs.length > 0; + return ( @@ -114,12 +120,18 @@ export const LaunchWorkflowForm: React.FC = props => { {...state.inputLoadingState} >
- + {state.unsupportedRequiredInputs.length > 0 ? ( + + ) : ( + + )}
) : null} @@ -143,10 +155,7 @@ export const LaunchWorkflowForm: React.FC = props => {