From 11e791b0d1d3bf2ec0402b358c4d86d6216d0bd2 Mon Sep 17 00:00:00 2001
From: Randy Schott <1815175+schottra@users.noreply.github.com>
Date: Wed, 21 Oct 2020 17:02:12 -0700
Subject: [PATCH 1/2] feat: support schema as a launch input type
---
.../Launch/LaunchForm/LaunchFormInputs.tsx | 1 -
.../Launch/LaunchForm/SimpleInput.tsx | 1 +
.../Launch/LaunchForm/__mocks__/utils.ts | 31 ++
src/components/Launch/LaunchForm/constants.ts | 2 +-
.../LaunchForm/inputHelpers/constants.ts | 1 +
.../inputHelpers/getHelperForInput.ts | 3 +-
.../Launch/LaunchForm/inputHelpers/schema.ts | 30 ++
.../inputHelpers/test/inputHelpers.test.ts | 150 +++++----
.../LaunchForm/inputHelpers/test/testCases.ts | 292 ++++++++++++------
.../inputHelpers/test/utils.test.ts | 66 ++--
.../Launch/LaunchForm/inputHelpers/utils.ts | 2 +-
src/components/Launch/LaunchForm/types.ts | 2 +
src/components/Launch/LaunchForm/utils.ts | 48 ++-
13 files changed, 413 insertions(+), 216 deletions(-)
create mode 100644 src/components/Launch/LaunchForm/inputHelpers/schema.ts
diff --git a/src/components/Launch/LaunchForm/LaunchFormInputs.tsx b/src/components/Launch/LaunchForm/LaunchFormInputs.tsx
index 01ae9adb5..2dbf5cab8 100644
--- a/src/components/Launch/LaunchForm/LaunchFormInputs.tsx
+++ b/src/components/Launch/LaunchForm/LaunchFormInputs.tsx
@@ -23,7 +23,6 @@ function getComponentForInput(input: InputProps, showErrors: boolean) {
case InputType.Collection:
return ;
case InputType.Map:
- case InputType.Schema:
case InputType.Unknown:
case InputType.None:
return ;
diff --git a/src/components/Launch/LaunchForm/SimpleInput.tsx b/src/components/Launch/LaunchForm/SimpleInput.tsx
index 5f4aa8f5f..058f9fbf5 100644
--- a/src/components/Launch/LaunchForm/SimpleInput.tsx
+++ b/src/components/Launch/LaunchForm/SimpleInput.tsx
@@ -55,6 +55,7 @@ export const SimpleInput: React.FC = props => {
);
case InputType.Datetime:
return ;
+ case InputType.Schema:
case InputType.String:
case InputType.Integer:
case InputType.Float:
diff --git a/src/components/Launch/LaunchForm/__mocks__/utils.ts b/src/components/Launch/LaunchForm/__mocks__/utils.ts
index 4b41bf796..2d5efcad8 100644
--- a/src/components/Launch/LaunchForm/__mocks__/utils.ts
+++ b/src/components/Launch/LaunchForm/__mocks__/utils.ts
@@ -1,5 +1,6 @@
import { Core } from 'flyteidl';
import { BlobDimensionality } from 'models';
+import { InputType, InputTypeDefinition } from '../types';
export function primitiveLiteral(primitive: Core.IPrimitive): Core.ILiteral {
return { scalar: { primitive } };
@@ -20,3 +21,33 @@ export function blobLiteral({
}
};
}
+
+export function collectionInputTypeDefinition(
+ typeDefinition: InputTypeDefinition
+): InputTypeDefinition {
+ return {
+ literalType: {
+ collectionType: typeDefinition.literalType
+ },
+ type: InputType.Collection,
+ subtype: typeDefinition
+ };
+}
+
+export function nestedCollectionInputTypeDefinition(
+ typeDefinition: InputTypeDefinition
+): InputTypeDefinition {
+ return {
+ literalType: {
+ collectionType: {
+ collectionType: typeDefinition.literalType
+ }
+ },
+ type: InputType.Collection,
+ subtype: {
+ literalType: { collectionType: typeDefinition.literalType },
+ type: InputType.Collection,
+ subtype: typeDefinition
+ }
+ };
+}
diff --git a/src/components/Launch/LaunchForm/constants.ts b/src/components/Launch/LaunchForm/constants.ts
index 31f18c1e4..398a79761 100644
--- a/src/components/Launch/LaunchForm/constants.ts
+++ b/src/components/Launch/LaunchForm/constants.ts
@@ -36,7 +36,7 @@ export const typeLabels: { [k in InputType]: string } = {
[InputType.Integer]: 'integer',
[InputType.Map]: '',
[InputType.None]: 'none',
- [InputType.Schema]: 'schema',
+ [InputType.Schema]: 'schema - uri',
[InputType.String]: 'string',
[InputType.Struct]: 'struct',
[InputType.Unknown]: 'unknown'
diff --git a/src/components/Launch/LaunchForm/inputHelpers/constants.ts b/src/components/Launch/LaunchForm/inputHelpers/constants.ts
index 15aad09e3..0951a3a89 100644
--- a/src/components/Launch/LaunchForm/inputHelpers/constants.ts
+++ b/src/components/Launch/LaunchForm/inputHelpers/constants.ts
@@ -8,6 +8,7 @@ export function literalNone(): Core.ILiteral {
export const allowedDateFormats = [ISO_8601, RFC_2822];
const primitivePath = 'scalar.primitive';
+export const schemaUriPath = 'scalar.schema.uri';
/** Strings constants which can be used to perform a deep `get` on a scalar
* literal type using a primitive value.
diff --git a/src/components/Launch/LaunchForm/inputHelpers/getHelperForInput.ts b/src/components/Launch/LaunchForm/inputHelpers/getHelperForInput.ts
index ef6c69ec8..d58d8b191 100644
--- a/src/components/Launch/LaunchForm/inputHelpers/getHelperForInput.ts
+++ b/src/components/Launch/LaunchForm/inputHelpers/getHelperForInput.ts
@@ -7,6 +7,7 @@ import { durationHelper } from './duration';
import { floatHelper } from './float';
import { integerHelper } from './integer';
import { noneHelper } from './none';
+import { schemaHelper } from './schema';
import { stringHelper } from './string';
import { InputHelper } from './types';
@@ -25,7 +26,7 @@ const inputHelpers: Record = {
[InputType.Integer]: integerHelper,
[InputType.Map]: unsupportedHelper,
[InputType.None]: noneHelper,
- [InputType.Schema]: unsupportedHelper,
+ [InputType.Schema]: schemaHelper,
[InputType.String]: stringHelper,
[InputType.Struct]: unsupportedHelper,
[InputType.Unknown]: unsupportedHelper
diff --git a/src/components/Launch/LaunchForm/inputHelpers/schema.ts b/src/components/Launch/LaunchForm/inputHelpers/schema.ts
new file mode 100644
index 000000000..5533144bb
--- /dev/null
+++ b/src/components/Launch/LaunchForm/inputHelpers/schema.ts
@@ -0,0 +1,30 @@
+import { Core } from 'flyteidl';
+import { InputValue } from '../types';
+import { schemaUriPath } from './constants';
+import { ConverterInput, InputHelper, InputValidatorParams } from './types';
+import { extractLiteralWithCheck } from './utils';
+
+function fromLiteral(literal: Core.ILiteral): InputValue {
+ return extractLiteralWithCheck(literal, schemaUriPath);
+}
+
+function toLiteral({ typeDefinition, value }: ConverterInput): Core.ILiteral {
+ const uri = typeof value === 'string' ? value : value.toString();
+ // Note: schema type may be undefined if user is working with a generic schema.
+ const {
+ literalType: { schema: type }
+ } = typeDefinition;
+ return { scalar: { schema: { type, uri } } };
+}
+
+function validate({ value }: InputValidatorParams) {
+ if (typeof value !== 'string') {
+ throw new Error('Value is not a string');
+ }
+}
+
+export const schemaHelper: InputHelper = {
+ fromLiteral,
+ toLiteral,
+ validate
+};
diff --git a/src/components/Launch/LaunchForm/inputHelpers/test/inputHelpers.test.ts b/src/components/Launch/LaunchForm/inputHelpers/test/inputHelpers.test.ts
index b62c6211a..f845e4fd1 100644
--- a/src/components/Launch/LaunchForm/inputHelpers/test/inputHelpers.test.ts
+++ b/src/components/Launch/LaunchForm/inputHelpers/test/inputHelpers.test.ts
@@ -1,8 +1,12 @@
import { Core } from 'flyteidl';
import * as Long from 'long';
import { BlobDimensionality } from 'models';
-import { primitiveLiteral } from '../../__mocks__/utils';
-import { InputProps, InputType } from '../../types';
+import {
+ collectionInputTypeDefinition,
+ nestedCollectionInputTypeDefinition,
+ primitiveLiteral
+} from '../../__mocks__/utils';
+import { InputProps, InputType, InputTypeDefinition } from '../../types';
import { literalNone } from '../constants';
import { getHelperForInput } from '../getHelperForInput';
import {
@@ -12,6 +16,7 @@ import {
} from '../inputHelpers';
import { collectionChildToString } from '../utils';
import {
+ inputTypes,
literalTestCases,
literalToInputTestCases,
supportedPrimitives,
@@ -25,69 +30,80 @@ const baseInputProps: InputProps = {
name: '',
onChange: () => {},
required: false,
- typeDefinition: { type: InputType.Unknown }
+ typeDefinition: inputTypes.unknown
};
-function makeSimpleInput(type: InputType, value: any): InputProps {
- return { ...baseInputProps, value, typeDefinition: { type } };
+function makeSimpleInput(
+ typeDefinition: InputTypeDefinition,
+ value: any
+): InputProps {
+ return { ...baseInputProps, value, typeDefinition };
}
-function makeCollectionInput(type: InputType, value: string): InputProps {
+function makeCollectionInput(
+ typeDefinition: InputTypeDefinition,
+ value: string
+): InputProps {
return {
...baseInputProps,
value,
- typeDefinition: { type: InputType.Collection, subtype: { type } }
+ typeDefinition: collectionInputTypeDefinition(typeDefinition)
};
}
-function makeNestedCollectionInput(type: InputType, value: string): InputProps {
+function makeNestedCollectionInput(
+ typeDefinition: InputTypeDefinition,
+ value: string
+): InputProps {
return {
...baseInputProps,
value,
- typeDefinition: {
- type: InputType.Collection,
- subtype: { type: InputType.Collection, subtype: { type } }
- }
+ typeDefinition: nestedCollectionInputTypeDefinition(typeDefinition)
};
}
describe('literalToInputValue', () => {
describe('Primitives', () => {
- literalToInputTestCases.map(([type, input, output]) =>
- it(`should correctly convert ${type}: ${JSON.stringify(
- input
- )}`, () =>
- expect(literalToInputValue({ type }, input)).toEqual(output))
+ literalToInputTestCases.map(([typeDefinition, input, output]) =>
+ it(`should correctly convert ${
+ typeDefinition.type
+ }: ${JSON.stringify(input)}`, () =>
+ expect(literalToInputValue(typeDefinition, input)).toEqual(
+ output
+ ))
);
- supportedPrimitives.map(type =>
- it(`should convert None value for ${type} to undefined`, () =>
+ supportedPrimitives.map(typeDefinition =>
+ it(`should convert None value for ${typeDefinition.type} to undefined`, () =>
expect(
- literalToInputValue({ type }, literalNone())
+ literalToInputValue(typeDefinition, literalNone())
).toBeUndefined())
);
it('should correctly convert noneType to undefined', () =>
- expect(
- literalToInputValue({ type: InputType.None }, literalNone())
- ).toEqual(undefined));
+ expect(literalToInputValue(inputTypes.none, literalNone())).toEqual(
+ undefined
+ ));
});
describe('Collections', () => {
- literalToInputTestCases.map(([type, input, output]) => {
- it(`should correctly convert collection of ${type}: ${JSON.stringify(
- input
- )}`, () => {
+ literalToInputTestCases.map(([typeDefinition, input, output]) => {
+ it(`should correctly convert collection of ${
+ typeDefinition.type
+ }: ${JSON.stringify(input)}`, () => {
const collection: Core.ILiteral = {
collection: {
// Duplicate it to test comma separation
literals: [input, input]
}
};
- const stringifiedValue = collectionChildToString(type, output);
+ const stringifiedValue = collectionChildToString(
+ typeDefinition.type,
+ output
+ );
const expectedString = `[${stringifiedValue},${stringifiedValue}]`;
const result = literalToInputValue(
- { type: InputType.Collection, subtype: { type } },
+ collectionInputTypeDefinition(typeDefinition),
collection
);
expect(result).toEqual(expectedString);
@@ -102,12 +118,14 @@ describe('literalToInputValue', () => {
}
};
+ const typeDefinition: InputTypeDefinition = {
+ literalType: { simple: Core.SimpleType.NONE },
+ type: InputType.None
+ };
+
expect(
literalToInputValue(
- {
- type: InputType.Collection,
- subtype: { type: InputType.None }
- },
+ collectionInputTypeDefinition(typeDefinition),
collection
)
).toEqual('[]');
@@ -118,7 +136,7 @@ describe('literalToInputValue', () => {
const { defaultValue } = getHelperForInput(InputType.Boolean);
expect(
literalToInputValue(
- { type: InputType.Boolean },
+ inputTypes.boolean,
// Invalid boolean input value because it uses the string field
{ scalar: { primitive: { stringValue: 'whoops' } } }
)
@@ -128,18 +146,18 @@ describe('literalToInputValue', () => {
describe('inputToLiteral', () => {
describe('Scalars', () => {
- literalTestCases.map(([type, input, output]) => {
- it(`should correctly convert ${type}: ${JSON.stringify(
- input
- )} (${typeof input})`, () =>
- expect(inputToLiteral(makeSimpleInput(type, input))).toEqual(
- output
- ));
+ literalTestCases.map(([typeDefinition, input, output]) => {
+ it(`should correctly convert ${
+ typeDefinition.type
+ }: ${JSON.stringify(input)} (${typeof input})`, () =>
+ expect(
+ inputToLiteral(makeSimpleInput(typeDefinition, input))
+ ).toEqual(output));
});
});
describe('Collections', () => {
- literalTestCases.map(([type, input, output]) => {
+ literalTestCases.map(([typeDefinition, input, output]) => {
let value: any;
if (['boolean', 'number'].includes(typeof input)) {
value = input;
@@ -153,20 +171,20 @@ describe('inputToLiteral', () => {
value = JSON.stringify(input);
}
- it(`should correctly convert collection of type ${type}: [${JSON.stringify(
- value
- )}] (${typeof input})`, () => {
+ it(`should correctly convert collection of type ${
+ typeDefinition.type
+ }: [${JSON.stringify(value)}] (${typeof input})`, () => {
const result = inputToLiteral(
- makeCollectionInput(type, `[${value}]`)
+ makeCollectionInput(typeDefinition, `[${value}]`)
);
expect(result.collection!.literals![0]).toEqual(output);
});
- it(`should correctly convert nested collection of type ${type}: [[${JSON.stringify(
- value
- )}]] (${typeof input})`, () => {
+ it(`should correctly convert nested collection of type ${
+ typeDefinition.type
+ }: [[${JSON.stringify(value)}]] (${typeof input})`, () => {
const result = inputToLiteral(
- makeNestedCollectionInput(type, `[[${value}]]`)
+ makeNestedCollectionInput(typeDefinition, `[[${value}]]`)
);
expect(
result.collection!.literals![0].collection!.literals![0]
@@ -176,10 +194,10 @@ describe('inputToLiteral', () => {
});
describe('Unsupported Types', () => {
- unsupportedTypes.map(type =>
- it(`should return empty value for type: ${type}`, () => {
+ unsupportedTypes.map(typeDefinition =>
+ it(`should return empty value for type: ${typeDefinition.type}`, () => {
expect(
- inputToLiteral(makeSimpleInput(type, '')).scalar
+ inputToLiteral(makeSimpleInput(typeDefinition, '')).scalar
).toEqual({ noneType: {} });
})
);
@@ -187,7 +205,7 @@ describe('inputToLiteral', () => {
it('Should return initial value for inputs with no value', () => {
const simpleInput = makeSimpleInput(
- InputType.String,
+ inputTypes.string,
primitiveLiteral({ stringValue: '' })
);
const initialValue = primitiveLiteral({ stringValue: 'abcdefg' });
@@ -199,14 +217,14 @@ describe('inputToLiteral', () => {
});
function generateValidityTests(
- type: InputType,
+ typeDefinition: InputTypeDefinition,
{ valid, invalid }: { valid: any[]; invalid: any[] }
) {
valid.map(value =>
it(`should treat ${JSON.stringify(
value
)} (${typeof value}) as valid`, () => {
- const input = makeSimpleInput(type, value);
+ const input = makeSimpleInput(typeDefinition, value);
expect(() => validateInput(input)).not.toThrowError();
})
);
@@ -214,38 +232,38 @@ function generateValidityTests(
it(`should treat ${JSON.stringify(
value
)} (${typeof value}) as invalid`, () => {
- const input = makeSimpleInput(type, value);
+ const input = makeSimpleInput(typeDefinition, value);
expect(() => validateInput(input)).toThrowError();
})
);
}
describe('validateInput', () => {
describe('boolean', () => {
- generateValidityTests(InputType.Boolean, validityTestCases.boolean);
+ generateValidityTests(inputTypes.boolean, validityTestCases.boolean);
});
describe('blob', () => {
- generateValidityTests(InputType.Blob, validityTestCases.blob);
+ generateValidityTests(inputTypes.blobSingle, validityTestCases.blob);
});
describe('datetime', () => {
- generateValidityTests(InputType.Datetime, validityTestCases.datetime);
+ generateValidityTests(inputTypes.datetime, validityTestCases.datetime);
});
describe('duration', () => {
- generateValidityTests(InputType.Duration, validityTestCases.duration);
+ generateValidityTests(inputTypes.duration, validityTestCases.duration);
});
describe('float', () => {
- generateValidityTests(InputType.Float, validityTestCases.float);
+ generateValidityTests(inputTypes.float, validityTestCases.float);
});
describe('integer', () => {
- generateValidityTests(InputType.Integer, validityTestCases.integer);
+ generateValidityTests(inputTypes.integer, validityTestCases.integer);
});
describe('string', () => {
- generateValidityTests(InputType.String, validityTestCases.string);
+ generateValidityTests(inputTypes.string, validityTestCases.string);
});
it('should throw errors for missing required simple values', () => {
@@ -258,7 +276,7 @@ describe('validateInput', () => {
it('should throw errors for missing required Blob values', () => {
// URI is the only required, user-provided value with no default
- const simpleInput = makeSimpleInput(InputType.Blob, {
+ const simpleInput = makeSimpleInput(inputTypes.blobSingle, {
format: 'csv',
dimensionality: BlobDimensionality.SINGLE
});
@@ -268,7 +286,7 @@ describe('validateInput', () => {
it('should not throw an error for a required input with an initial value and no value', () => {
const simpleInput = makeSimpleInput(
- InputType.String,
+ inputTypes.string,
primitiveLiteral({ stringValue: '' })
);
simpleInput.required = true;
diff --git a/src/components/Launch/LaunchForm/inputHelpers/test/testCases.ts b/src/components/Launch/LaunchForm/inputHelpers/test/testCases.ts
index 99c69a215..e79b8b1b7 100644
--- a/src/components/Launch/LaunchForm/inputHelpers/test/testCases.ts
+++ b/src/components/Launch/LaunchForm/inputHelpers/test/testCases.ts
@@ -1,32 +1,118 @@
import { dateToTimestamp, millisecondsToDuration } from 'common/utils';
import { Core } from 'flyteidl';
import * as Long from 'long';
-import { BlobDimensionality } from 'models';
+import { BlobDimensionality, SchemaColumnType } from 'models';
import { blobLiteral, primitiveLiteral } from '../../__mocks__/utils';
-import { InputType, InputValue } from '../../types';
+import { InputType, InputTypeDefinition, InputValue } from '../../types';
import { literalNone } from '../constants';
// Defines type of value, input, and expected value of a `Core.ILiteral`
-type LiteralTestParams = [InputType, any, Core.ILiteral];
+type LiteralTestParams = [InputTypeDefinition, any, Core.ILiteral];
+
+type InputTypeKey =
+ | 'binary'
+ | 'boolean'
+ | 'blobSingle'
+ | 'blobMulti'
+ | 'datetime'
+ | 'duration'
+ | 'error'
+ | 'integer'
+ | 'float'
+ | 'map'
+ | 'none'
+ | 'schema'
+ | 'string'
+ | 'struct'
+ | 'unknown';
+export const inputTypes: Record = {
+ binary: {
+ literalType: { simple: Core.SimpleType.BINARY },
+ type: InputType.Binary
+ },
+ boolean: {
+ literalType: { simple: Core.SimpleType.BOOLEAN },
+ type: InputType.Boolean
+ },
+ blobSingle: {
+ literalType: { blob: { dimensionality: BlobDimensionality.SINGLE } },
+ type: InputType.Blob
+ },
+ blobMulti: {
+ literalType: { blob: { dimensionality: BlobDimensionality.MULTIPART } },
+ type: InputType.Blob
+ },
+ datetime: {
+ literalType: { simple: Core.SimpleType.DATETIME },
+ type: InputType.Datetime
+ },
+ duration: {
+ literalType: { simple: Core.SimpleType.DURATION },
+ type: InputType.Duration
+ },
+ error: {
+ literalType: { simple: Core.SimpleType.ERROR },
+ type: InputType.Error
+ },
+ integer: {
+ literalType: { simple: Core.SimpleType.INTEGER },
+ type: InputType.Integer
+ },
+ float: {
+ literalType: { simple: Core.SimpleType.FLOAT },
+ type: InputType.Float
+ },
+ map: {
+ literalType: {
+ mapValueType: { simple: Core.SimpleType.STRING }
+ },
+ type: InputType.Map
+ },
+ none: {
+ literalType: { simple: Core.SimpleType.NONE },
+ type: InputType.None
+ },
+ schema: {
+ literalType: {
+ schema: {
+ columns: [{ name: 'column1', type: SchemaColumnType.STRING }]
+ }
+ },
+ type: InputType.Schema
+ },
+ string: {
+ literalType: { simple: Core.SimpleType.STRING },
+ type: InputType.String
+ },
+ struct: {
+ literalType: { simple: Core.SimpleType.STRUCT },
+ type: InputType.Struct
+ },
+ unknown: {
+ literalType: { simple: Core.SimpleType.NONE },
+ type: InputType.Unknown
+ }
+};
const validDateString = '2019-01-10T00:00:00.000Z'; // Dec 1, 2019
-export const supportedPrimitives = [
- InputType.Boolean,
- InputType.Blob,
- InputType.Datetime,
- InputType.Duration,
- InputType.Float,
- InputType.Integer
+export const supportedPrimitives: InputTypeDefinition[] = [
+ inputTypes.boolean,
+ inputTypes.blobSingle,
+ inputTypes.blobMulti,
+ inputTypes.datetime,
+ inputTypes.duration,
+ inputTypes.float,
+ inputTypes.integer,
+ inputTypes.schema
];
-export const unsupportedTypes = [
- InputType.Binary,
- InputType.Error,
- InputType.Map,
- InputType.None,
- InputType.Schema,
- InputType.Struct
+export const unsupportedTypes: InputTypeDefinition[] = [
+ inputTypes.binary,
+ inputTypes.error,
+ inputTypes.map,
+ inputTypes.none,
+ inputTypes.struct
];
export const validityTestCases = {
@@ -111,98 +197,114 @@ export const validityTestCases = {
};
export const literalTestCases: LiteralTestParams[] = [
- [InputType.Boolean, true, primitiveLiteral({ boolean: true })],
- [InputType.Boolean, 'true', primitiveLiteral({ boolean: true })],
- [InputType.Boolean, 't', primitiveLiteral({ boolean: true })],
- [InputType.Boolean, '1', primitiveLiteral({ boolean: true })],
- [InputType.Boolean, 1, primitiveLiteral({ boolean: true })],
- [InputType.Boolean, false, primitiveLiteral({ boolean: false })],
- [InputType.Boolean, 'false', primitiveLiteral({ boolean: false })],
- [InputType.Boolean, 'f', primitiveLiteral({ boolean: false })],
- [InputType.Boolean, '0', primitiveLiteral({ boolean: false })],
- [InputType.Boolean, 0, primitiveLiteral({ boolean: false })],
- [
- InputType.Datetime,
+ [inputTypes.boolean, true, primitiveLiteral({ boolean: true })],
+ [inputTypes.boolean, 'true', primitiveLiteral({ boolean: true })],
+ [inputTypes.boolean, 't', primitiveLiteral({ boolean: true })],
+ [inputTypes.boolean, '1', primitiveLiteral({ boolean: true })],
+ [inputTypes.boolean, 1, primitiveLiteral({ boolean: true })],
+ [inputTypes.boolean, false, primitiveLiteral({ boolean: false })],
+ [inputTypes.boolean, 'false', primitiveLiteral({ boolean: false })],
+ [inputTypes.boolean, 'f', primitiveLiteral({ boolean: false })],
+ [inputTypes.boolean, '0', primitiveLiteral({ boolean: false })],
+ [inputTypes.boolean, 0, primitiveLiteral({ boolean: false })],
+ [
+ inputTypes.datetime,
new Date(validDateString),
primitiveLiteral({
datetime: dateToTimestamp(new Date(validDateString))
})
],
[
- InputType.Datetime,
+ inputTypes.datetime,
validDateString,
primitiveLiteral({
datetime: dateToTimestamp(new Date(validDateString))
})
],
[
- InputType.Duration,
+ inputTypes.duration,
0,
primitiveLiteral({ duration: millisecondsToDuration(0) })
],
[
- InputType.Duration,
+ inputTypes.duration,
10000,
primitiveLiteral({ duration: millisecondsToDuration(10000) })
],
- [InputType.Float, 0, primitiveLiteral({ floatValue: 0 })],
- [InputType.Float, '0', primitiveLiteral({ floatValue: 0 })],
- [InputType.Float, -1.5, primitiveLiteral({ floatValue: -1.5 })],
- [InputType.Float, '-1.5', primitiveLiteral({ floatValue: -1.5 })],
- [InputType.Float, 1.5, primitiveLiteral({ floatValue: 1.5 })],
- [InputType.Float, '1.5', primitiveLiteral({ floatValue: 1.5 })],
- [InputType.Float, 1.25e10, primitiveLiteral({ floatValue: 1.25e10 })],
- [InputType.Float, '1.25e10', primitiveLiteral({ floatValue: 1.25e10 })],
- [InputType.Integer, 0, primitiveLiteral({ integer: Long.fromNumber(0) })],
- [
- InputType.Integer,
+ [inputTypes.float, 0, primitiveLiteral({ floatValue: 0 })],
+ [inputTypes.float, '0', primitiveLiteral({ floatValue: 0 })],
+ [inputTypes.float, -1.5, primitiveLiteral({ floatValue: -1.5 })],
+ [inputTypes.float, '-1.5', primitiveLiteral({ floatValue: -1.5 })],
+ [inputTypes.float, 1.5, primitiveLiteral({ floatValue: 1.5 })],
+ [inputTypes.float, '1.5', primitiveLiteral({ floatValue: 1.5 })],
+ [inputTypes.float, 1.25e10, primitiveLiteral({ floatValue: 1.25e10 })],
+ [inputTypes.float, '1.25e10', primitiveLiteral({ floatValue: 1.25e10 })],
+ [inputTypes.integer, 0, primitiveLiteral({ integer: Long.fromNumber(0) })],
+ [
+ inputTypes.integer,
Long.fromNumber(0),
primitiveLiteral({ integer: Long.fromNumber(0) })
],
- [InputType.Integer, '0', primitiveLiteral({ integer: Long.fromNumber(0) })],
- [InputType.Integer, 1, primitiveLiteral({ integer: Long.fromNumber(1) })],
[
- InputType.Integer,
+ inputTypes.integer,
+ '0',
+ primitiveLiteral({ integer: Long.fromNumber(0) })
+ ],
+ [inputTypes.integer, 1, primitiveLiteral({ integer: Long.fromNumber(1) })],
+ [
+ inputTypes.integer,
Long.fromNumber(1),
primitiveLiteral({ integer: Long.fromNumber(1) })
],
- [InputType.Integer, '1', primitiveLiteral({ integer: Long.fromNumber(1) })],
- [InputType.Integer, -1, primitiveLiteral({ integer: Long.fromNumber(-1) })],
[
- InputType.Integer,
+ inputTypes.integer,
+ '1',
+ primitiveLiteral({ integer: Long.fromNumber(1) })
+ ],
+ [
+ inputTypes.integer,
+ -1,
+ primitiveLiteral({ integer: Long.fromNumber(-1) })
+ ],
+ [
+ inputTypes.integer,
Long.fromNumber(-1),
primitiveLiteral({ integer: Long.fromNumber(-1) })
],
[
- InputType.Integer,
+ inputTypes.integer,
'-1',
primitiveLiteral({ integer: Long.fromNumber(-1) })
],
[
- InputType.Integer,
+ inputTypes.integer,
Long.MAX_VALUE.toString(),
primitiveLiteral({ integer: Long.MAX_VALUE })
],
[
- InputType.Integer,
+ inputTypes.integer,
Long.MAX_VALUE,
primitiveLiteral({ integer: Long.MAX_VALUE })
],
[
- InputType.Integer,
+ inputTypes.integer,
Long.MIN_VALUE.toString(),
primitiveLiteral({ integer: Long.MIN_VALUE })
],
[
- InputType.Integer,
+ inputTypes.integer,
Long.MIN_VALUE,
primitiveLiteral({ integer: Long.MIN_VALUE })
],
- [InputType.String, '', primitiveLiteral({ stringValue: '' })],
- [InputType.String, 'abcdefg', primitiveLiteral({ stringValue: 'abcdefg' })],
+ [inputTypes.string, '', primitiveLiteral({ stringValue: '' })],
+ [
+ inputTypes.string,
+ 'abcdefg',
+ primitiveLiteral({ stringValue: 'abcdefg' })
+ ],
// Standard Blob
[
- InputType.Blob,
+ inputTypes.blobSingle,
{
uri: 's3://somePath',
format: 'csv',
@@ -216,7 +318,7 @@ export const literalTestCases: LiteralTestParams[] = [
],
// Multi-part blob
[
- InputType.Blob,
+ inputTypes.blobSingle,
{
dimensionality: BlobDimensionality.MULTIPART,
format: 'csv',
@@ -230,7 +332,7 @@ export const literalTestCases: LiteralTestParams[] = [
],
// Blob with missing format
[
- InputType.Blob,
+ inputTypes.blobSingle,
{
dimensionality: BlobDimensionality.SINGLE,
uri: 's3://somePath'
@@ -242,7 +344,7 @@ export const literalTestCases: LiteralTestParams[] = [
],
// Blob with empty format string
[
- InputType.Blob,
+ inputTypes.blobSingle,
{
dimensionality: BlobDimensionality.SINGLE,
format: '',
@@ -255,7 +357,7 @@ export const literalTestCases: LiteralTestParams[] = [
],
// Blobs using lowercase string for dimensionality
[
- InputType.Blob,
+ inputTypes.blobSingle,
{
dimensionality: 'single',
uri: 's3://somePath'
@@ -266,7 +368,7 @@ export const literalTestCases: LiteralTestParams[] = [
})
],
[
- InputType.Blob,
+ inputTypes.blobMulti,
{
dimensionality: 'multipart',
uri: 's3://somePath'
@@ -278,7 +380,7 @@ export const literalTestCases: LiteralTestParams[] = [
],
// Blobs using uppercase string for dimensionality
[
- InputType.Blob,
+ inputTypes.blobMulti,
{
dimensionality: 'SINGLE',
uri: 's3://somePath'
@@ -289,7 +391,7 @@ export const literalTestCases: LiteralTestParams[] = [
})
],
[
- InputType.Blob,
+ inputTypes.blobMulti,
{
dimensionality: 'MULTIPART',
uri: 's3://somePath'
@@ -301,7 +403,7 @@ export const literalTestCases: LiteralTestParams[] = [
],
// Blob missing URI (results in None)
[
- InputType.Blob,
+ inputTypes.blobMulti,
{
format: 'csv',
dimensionality: 'MULTIPART'
@@ -309,66 +411,78 @@ export const literalTestCases: LiteralTestParams[] = [
literalNone()
],
// Blob which is not an object (results in None)
- [InputType.Blob, undefined, literalNone()]
+ [inputTypes.blobMulti, undefined, literalNone()]
];
type InputToLiteralTestParams = [
- InputType,
+ InputTypeDefinition,
Core.ILiteral,
InputValue | undefined
];
export const literalToInputTestCases: InputToLiteralTestParams[] = [
- [InputType.Boolean, primitiveLiteral({ boolean: true }), true],
- [InputType.Boolean, primitiveLiteral({ boolean: false }), false],
+ [inputTypes.boolean, primitiveLiteral({ boolean: true }), true],
+ [inputTypes.boolean, primitiveLiteral({ boolean: false }), false],
[
- InputType.Datetime,
+ inputTypes.datetime,
primitiveLiteral({
datetime: dateToTimestamp(new Date(validDateString))
}),
validDateString
],
[
- InputType.Duration,
+ inputTypes.duration,
primitiveLiteral({ duration: millisecondsToDuration(0) }),
0
],
[
- InputType.Duration,
+ inputTypes.duration,
primitiveLiteral({ duration: millisecondsToDuration(10000) }),
10000
],
[
- InputType.Duration,
+ inputTypes.duration,
primitiveLiteral({ duration: millisecondsToDuration(1.5) }),
1.5
],
- [InputType.Float, primitiveLiteral({ floatValue: 0 }), 0],
- [InputType.Float, primitiveLiteral({ floatValue: -1.5 }), -1.5],
- [InputType.Float, primitiveLiteral({ floatValue: 1.5 }), 1.5],
- [InputType.Float, primitiveLiteral({ floatValue: 1.25e10 }), 1.25e10],
+ [inputTypes.float, primitiveLiteral({ floatValue: 0 }), 0],
+ [inputTypes.float, primitiveLiteral({ floatValue: -1.5 }), -1.5],
+ [inputTypes.float, primitiveLiteral({ floatValue: 1.5 }), 1.5],
+ [inputTypes.float, primitiveLiteral({ floatValue: 1.25e10 }), 1.25e10],
// Integers will be returned as strings because they may overflow numbers
- [InputType.Integer, primitiveLiteral({ integer: Long.fromNumber(0) }), '0'],
- [InputType.Integer, primitiveLiteral({ integer: Long.fromNumber(1) }), '1'],
[
- InputType.Integer,
+ inputTypes.integer,
+ primitiveLiteral({ integer: Long.fromNumber(0) }),
+ '0'
+ ],
+ [
+ inputTypes.integer,
+ primitiveLiteral({ integer: Long.fromNumber(1) }),
+ '1'
+ ],
+ [
+ inputTypes.integer,
primitiveLiteral({ integer: Long.fromNumber(-1) }),
'-1'
],
[
- InputType.Integer,
+ inputTypes.integer,
primitiveLiteral({ integer: Long.MAX_VALUE }),
Long.MAX_VALUE.toString()
],
[
- InputType.Integer,
+ inputTypes.integer,
primitiveLiteral({ integer: Long.MIN_VALUE }),
Long.MIN_VALUE.toString()
],
- [InputType.String, primitiveLiteral({ stringValue: '' }), ''],
- [InputType.String, primitiveLiteral({ stringValue: 'abcdefg' }), 'abcdefg'],
+ [inputTypes.string, primitiveLiteral({ stringValue: '' }), ''],
+ [
+ inputTypes.string,
+ primitiveLiteral({ stringValue: 'abcdefg' }),
+ 'abcdefg'
+ ],
// Standard Blob case
[
- InputType.Blob,
+ inputTypes.blobSingle,
blobLiteral({
dimensionality: BlobDimensionality.SINGLE,
format: 'csv',
@@ -382,7 +496,7 @@ export const literalToInputTestCases: InputToLiteralTestParams[] = [
],
// Multipart blob
[
- InputType.Blob,
+ inputTypes.blobMulti,
blobLiteral({
dimensionality: BlobDimensionality.MULTIPART,
format: 'csv',
@@ -396,7 +510,7 @@ export const literalToInputTestCases: InputToLiteralTestParams[] = [
],
// Empty uri
[
- InputType.Blob,
+ inputTypes.blobSingle,
blobLiteral({
dimensionality: BlobDimensionality.SINGLE,
format: 'csv'
@@ -409,7 +523,7 @@ export const literalToInputTestCases: InputToLiteralTestParams[] = [
],
// Empty format string
[
- InputType.Blob,
+ inputTypes.blobSingle,
blobLiteral({
dimensionality: BlobDimensionality.SINGLE,
format: '',
@@ -422,7 +536,7 @@ export const literalToInputTestCases: InputToLiteralTestParams[] = [
],
// Missing dimensionality
[
- InputType.Blob,
+ inputTypes.blobSingle,
blobLiteral({
format: 'csv',
uri: 's3://somePath'
diff --git a/src/components/Launch/LaunchForm/inputHelpers/test/utils.test.ts b/src/components/Launch/LaunchForm/inputHelpers/test/utils.test.ts
index 6671d9e00..f26904117 100644
--- a/src/components/Launch/LaunchForm/inputHelpers/test/utils.test.ts
+++ b/src/components/Launch/LaunchForm/inputHelpers/test/utils.test.ts
@@ -1,4 +1,8 @@
-import { InputType, InputTypeDefinition } from '../../types';
+import {
+ collectionInputTypeDefinition,
+ nestedCollectionInputTypeDefinition
+} from '../../__mocks__/utils';
+import { InputTypeDefinition } from '../../types';
import { typeIsSupported } from '../utils';
import { supportedPrimitives, unsupportedTypes } from './testCases';
@@ -6,40 +10,40 @@ 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 },
+ ...supportedPrimitives.map(
+ typeDefinition => [
+ `supports type ${typeDefinition.type}`,
+ typeDefinition,
+ true
+ ]
+ ),
+ ...supportedPrimitives.map(
+ typeDefinition => [
+ `supports 1-dimension collection of type ${typeDefinition.type}`,
+ collectionInputTypeDefinition(typeDefinition),
+ true
+ ]
+ ),
+ ...supportedPrimitives.map(
+ typeDefinition => [
+ `supports 2-dimension collection of type: ${typeDefinition.type}`,
+ nestedCollectionInputTypeDefinition(typeDefinition),
+ true
+ ]
+ ),
+ ...unsupportedTypes.map(typeDefinition => [
+ `does NOT support type ${typeDefinition.type}`,
+ typeDefinition,
false
]),
- ...unsupportedTypes.map(type => [
- `does NOT support 1-dimension collection of type ${type}`,
- { type: InputType.Collection, subtype: { type } },
+ ...unsupportedTypes.map(typeDefinition => [
+ `does NOT support 1-dimension collection of type ${typeDefinition.type}`,
+ collectionInputTypeDefinition(typeDefinition),
false
]),
- ...unsupportedTypes.map(type => [
- `does NOT support 2-dimension collection of type: ${type}`,
- {
- type: InputType.Collection,
- subtype: { type: InputType.Collection, subtype: { type } }
- },
+ ...unsupportedTypes.map(typeDefinition => [
+ `does NOT support 2-dimension collection of type: ${typeDefinition.type}`,
+ nestedCollectionInputTypeDefinition(typeDefinition),
false
])
];
diff --git a/src/components/Launch/LaunchForm/inputHelpers/utils.ts b/src/components/Launch/LaunchForm/inputHelpers/utils.ts
index f520127da..4018e0b4e 100644
--- a/src/components/Launch/LaunchForm/inputHelpers/utils.ts
+++ b/src/components/Launch/LaunchForm/inputHelpers/utils.ts
@@ -38,7 +38,6 @@ export function typeIsSupported(typeDefinition: InputTypeDefinition): boolean {
case InputType.Error:
case InputType.Map:
case InputType.None:
- case InputType.Schema:
case InputType.Struct:
case InputType.Unknown:
return false;
@@ -48,6 +47,7 @@ export function typeIsSupported(typeDefinition: InputTypeDefinition): boolean {
case InputType.Duration:
case InputType.Float:
case InputType.Integer:
+ case InputType.Schema:
case InputType.String:
return true;
case InputType.Collection: {
diff --git a/src/components/Launch/LaunchForm/types.ts b/src/components/Launch/LaunchForm/types.ts
index eb3c9f679..35f8f2f23 100644
--- a/src/components/Launch/LaunchForm/types.ts
+++ b/src/components/Launch/LaunchForm/types.ts
@@ -3,6 +3,7 @@ import {
BlobDimensionality,
Identifier,
LaunchPlan,
+ LiteralType,
NamedEntityIdentifier,
Task,
Workflow,
@@ -152,6 +153,7 @@ export enum InputType {
}
export interface InputTypeDefinition {
+ literalType: LiteralType;
type: InputType;
subtype?: InputTypeDefinition;
}
diff --git a/src/components/Launch/LaunchForm/utils.ts b/src/components/Launch/LaunchForm/utils.ts
index ba1111dbd..6f24fd675 100644
--- a/src/components/Launch/LaunchForm/utils.ts
+++ b/src/components/Launch/LaunchForm/utils.ts
@@ -144,38 +144,34 @@ export function convertFormInputsToLiterals(
}
/** Converts a `LiteralType` to an `InputTypeDefintion` to assist with rendering
- * a type annotation.
+ * a type annotation and converting input values.
*/
export function getInputDefintionForLiteralType(
literalType: LiteralType
): InputTypeDefinition {
- if (literalType.blob) {
- return { type: InputType.Blob };
- }
-
- if (literalType.collectionType) {
- return {
- type: InputType.Collection,
- subtype: getInputDefintionForLiteralType(literalType.collectionType)
- };
- }
-
- if (literalType.mapValueType) {
- return {
- type: InputType.Map,
- subtype: getInputDefintionForLiteralType(literalType.mapValueType)
- };
- }
-
- if (literalType.schema) {
- return { type: InputType.Schema };
- }
+ const result: InputTypeDefinition = {
+ literalType,
+ type: InputType.Unknown
+ };
- if (literalType.simple) {
- return { type: simpleTypeToInputType[literalType.simple] };
+ if (literalType.blob) {
+ result.type = InputType.Blob;
+ } else if (literalType.collectionType) {
+ result.type = InputType.Collection;
+ result.subtype = getInputDefintionForLiteralType(
+ literalType.collectionType
+ );
+ } else if (literalType.mapValueType) {
+ result.type = InputType.Map;
+ result.subtype = getInputDefintionForLiteralType(
+ literalType.mapValueType
+ );
+ } else if (literalType.schema) {
+ result.type = InputType.Schema;
+ } else if (literalType.simple) {
+ result.type = simpleTypeToInputType[literalType.simple];
}
-
- return { type: InputType.Unknown };
+ return result;
}
export function getLaunchInputId(name: string): string {
From 568b984962bf64e13bf02abec4d029380a962aed Mon Sep 17 00:00:00 2001
From: Randy Schott <1815175+schottra@users.noreply.github.com>
Date: Wed, 21 Oct 2020 17:13:03 -0700
Subject: [PATCH 2/2] test: add value test cases for schema
---
.../inputHelpers/test/inputHelpers.test.ts | 4 +++
.../LaunchForm/inputHelpers/test/testCases.ts | 29 +++++++++++++++++++
2 files changed, 33 insertions(+)
diff --git a/src/components/Launch/LaunchForm/inputHelpers/test/inputHelpers.test.ts b/src/components/Launch/LaunchForm/inputHelpers/test/inputHelpers.test.ts
index f845e4fd1..7f0ab1cde 100644
--- a/src/components/Launch/LaunchForm/inputHelpers/test/inputHelpers.test.ts
+++ b/src/components/Launch/LaunchForm/inputHelpers/test/inputHelpers.test.ts
@@ -262,6 +262,10 @@ describe('validateInput', () => {
generateValidityTests(inputTypes.integer, validityTestCases.integer);
});
+ describe('schema', () => {
+ generateValidityTests(inputTypes.schema, validityTestCases.schema);
+ });
+
describe('string', () => {
generateValidityTests(inputTypes.string, validityTestCases.string);
});
diff --git a/src/components/Launch/LaunchForm/inputHelpers/test/testCases.ts b/src/components/Launch/LaunchForm/inputHelpers/test/testCases.ts
index e79b8b1b7..7a14b9b90 100644
--- a/src/components/Launch/LaunchForm/inputHelpers/test/testCases.ts
+++ b/src/components/Launch/LaunchForm/inputHelpers/test/testCases.ts
@@ -193,6 +193,8 @@ export const validityTestCases = {
Long.MIN_VALUE
]
},
+ // schema is just a specialized string input, so it has the same validity cases as string
+ schema: { invalid: [123, true, new Date(), {}], valid: ['', 'abcdefg'] },
string: { invalid: [123, true, new Date(), {}], valid: ['', 'abcdefg'] }
};
@@ -296,6 +298,27 @@ export const literalTestCases: LiteralTestParams[] = [
Long.MIN_VALUE,
primitiveLiteral({ integer: Long.MIN_VALUE })
],
+ [
+ inputTypes.schema,
+ '',
+ {
+ scalar: {
+ schema: { type: inputTypes.schema.literalType.schema, uri: '' }
+ }
+ }
+ ],
+ [
+ inputTypes.schema,
+ 's3://someUri',
+ {
+ scalar: {
+ schema: {
+ type: inputTypes.schema.literalType.schema,
+ uri: 's3://someUri'
+ }
+ }
+ }
+ ],
[inputTypes.string, '', primitiveLiteral({ stringValue: '' })],
[
inputTypes.string,
@@ -474,6 +497,12 @@ export const literalToInputTestCases: InputToLiteralTestParams[] = [
primitiveLiteral({ integer: Long.MIN_VALUE }),
Long.MIN_VALUE.toString()
],
+ [inputTypes.schema, { scalar: { schema: { uri: '' } } }, ''],
+ [
+ inputTypes.schema,
+ { scalar: { schema: { uri: 's3://someUri' } } },
+ 's3://someUri'
+ ],
[inputTypes.string, primitiveLiteral({ stringValue: '' }), ''],
[
inputTypes.string,