Skip to content

Commit

Permalink
feat: 0.8.0 release (#87)
Browse files Browse the repository at this point in the history
* fix: show full millisecond values for subsecond durations (#81)

* Bump npm from 6.14.5 to 6.14.6 (#82)

Bumps [npm](https://github.com/npm/cli) from 6.14.5 to 6.14.6.
- [Release notes](https://github.com/npm/cli/releases)
- [Changelog](https://github.com/npm/cli/blob/latest/CHANGELOG.md)
- [Commits](npm/cli@v6.14.5...v6.14.6)

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix: show info message for un-launchable workflows (#84)

* refactor: adding utility to help determine if an input type is supported

* fix: adding detection and special rendering for unlaunchable workflows

* fix: implementing error message for unlaunchable workflows

* chore: docs

* chore: pr feedback

* feat: Enables support for Blobs on the Launch form (#86)

* refactor: adds scaffolding for blob input support

* fix: add controls and processing for blob fields

* fix: support using string values for dimensionality in collections

* fix: more info so validators can check subfields

* test: tests for blob input helper

* test: remaining tests for blobs

* chore: cleanup

* refactor: adding some type guards

* test: fixing some test cases

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • Loading branch information
schottra and dependabot[bot] authored Jul 20, 2020
1 parent bed1b81 commit 65f2a81
Show file tree
Hide file tree
Showing 36 changed files with 1,103 additions and 168 deletions.
2 changes: 1 addition & 1 deletion src/common/formatters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export function millisecondsToHMS(valueMS: number): string {
}

if (valueMS < 1000) {
return subSecondString;
return `${valueMS}ms`;
}

const duration = moment.duration(valueMS);
Expand Down
4 changes: 2 additions & 2 deletions src/common/test/formatters.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ describe('formatDateLocalTimezone', () => {
const millisecondToHMSTestCases: [number, string][] = [
[-1, unknownValueString],
[0, zeroSecondsString],
[1, subSecondString],
[999, subSecondString],
[1, '1ms'],
[999, '999ms'],
[1000, '1s'],
[60000, '1m'],
[61000, '1m 1s'],
Expand Down
17 changes: 17 additions & 0 deletions src/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,20 @@ export function sortedObjectKeys(
): ReturnType<typeof Object.keys> {
return Object.keys(object).sort((a, b) => a.localeCompare(b));
}

/**
* Helper function for exhaustive checks of discriminated unions.
* https://basarat.gitbooks.io/typescript/docs/types/discriminated-unions.html
*/
export function assertNever(
value: never,
{ noThrow }: { noThrow?: boolean } = {}
): never {
if (noThrow) {
return value;
}

throw new Error(
`Unhandled discriminated union member: ${JSON.stringify(value)}`
);
}
126 changes: 126 additions & 0 deletions src/components/Launch/LaunchWorkflowForm/BlobInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import {
FormControl,
FormHelperText,
InputLabel,
MenuItem,
Select,
TextField,
Typography
} from '@material-ui/core';
import { makeStyles, Theme } from '@material-ui/core/styles';
import { BlobDimensionality } from 'models';
import * as React from 'react';
import {
blobFormatHelperText,
blobUriHelperText,
defaultBlobValue
} from './constants';
import { InputProps } from './types';
import { getLaunchInputId, isBlobValue } from './utils';

const useStyles = makeStyles((theme: Theme) => ({
dimensionalityInput: {
flex: '1 1 auto',
marginLeft: theme.spacing(2)
},
formatInput: {
flex: '1 1 auto'
},
inputContainer: {
borderLeft: `1px solid ${theme.palette.divider}`,
marginTop: theme.spacing(1),
paddingLeft: theme.spacing(1)
},
metadataContainer: {
display: 'flex',
marginTop: theme.spacing(1),
width: '100%'
}
}));

/** A micro form for entering the values related to a Blob Literal */
export const BlobInput: React.FC<InputProps> = props => {
const styles = useStyles();
const { error, label, name, onChange, value: propValue } = props;
const blobValue = isBlobValue(propValue) ? propValue : defaultBlobValue;
const hasError = !!error;
const helperText = hasError ? error : props.helperText;

const handleUriChange = ({
target: { value: uri }
}: React.ChangeEvent<HTMLInputElement>) => {
onChange({
...blobValue,
uri
});
};

const handleFormatChange = ({
target: { value: format }
}: React.ChangeEvent<HTMLInputElement>) => {
onChange({
...blobValue,
format
});
};

const handleDimensionalityChange = ({
target: { value }
}: React.ChangeEvent<{ value: unknown }>) => {
onChange({
...blobValue,
dimensionality: value as BlobDimensionality
});
};

const selectId = getLaunchInputId(`${name}-select`);

return (
<div>
<Typography variant="body1" component="label">
{label}
</Typography>
<FormHelperText error={hasError}>{helperText}</FormHelperText>
<div className={styles.inputContainer}>
<TextField
id={getLaunchInputId(`${name}-uri`)}
helperText={blobUriHelperText}
fullWidth={true}
label="uri"
onChange={handleUriChange}
value={blobValue.uri}
variant="outlined"
/>
<div className={styles.metadataContainer}>
<TextField
className={styles.formatInput}
id={getLaunchInputId(`${name}-format`)}
helperText={blobFormatHelperText}
label="format"
onChange={handleFormatChange}
value={blobValue.format}
variant="outlined"
/>
<FormControl className={styles.dimensionalityInput}>
<InputLabel id={`${selectId}-label`}>
Dimensionality
</InputLabel>
<Select
labelId={`${selectId}-label`}
id={selectId}
value={blobValue.dimensionality}
onChange={handleDimensionalityChange}
>
<MenuItem value={BlobDimensionality.SINGLE}>
Single (File)
</MenuItem>
<MenuItem value={BlobDimensionality.MULTIPART}>
Multipart (Directory)
</MenuItem>
</Select>
</FormControl>
</div>
</div>
</div>
);
};
31 changes: 20 additions & 11 deletions src/components/Launch/LaunchWorkflowForm/LaunchWorkflowForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -67,6 +68,11 @@ export const LaunchWorkflowForm: React.FC<LaunchWorkflowFormProps> = props => {
state.onSubmit();
};

const preventSubmit =
submissionState.loading ||
!state.inputLoadingState.hasLoaded ||
state.unsupportedRequiredInputs.length > 0;

return (
<InputValueCacheContext.Provider value={state.inputValueCache}>
<DialogTitle disableTypography={true} className={styles.header}>
Expand Down Expand Up @@ -114,12 +120,18 @@ export const LaunchWorkflowForm: React.FC<LaunchWorkflowFormProps> = props => {
{...state.inputLoadingState}
>
<section title={formStrings.inputs}>
<LaunchWorkflowFormInputs
key={state.formKey}
inputs={state.inputs}
ref={state.formInputsRef}
showErrors={state.showErrors}
/>
{state.unsupportedRequiredInputs.length > 0 ? (
<UnsupportedRequiredInputsError
inputs={state.unsupportedRequiredInputs}
/>
) : (
<LaunchWorkflowFormInputs
key={state.formKey}
inputs={state.inputs}
ref={state.formInputsRef}
showErrors={state.showErrors}
/>
)}
</section>
</WaitForData>
) : null}
Expand All @@ -143,10 +155,7 @@ export const LaunchWorkflowForm: React.FC<LaunchWorkflowFormProps> = props => {
</Button>
<Button
color="primary"
disabled={
submissionState.loading ||
!state.inputLoadingState.hasLoaded
}
disabled={preventSubmit}
id="launch-workflow-submit"
onClick={submit}
type="submit"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react';
import { BlobInput } from './BlobInput';
import { CollectionInput } from './CollectionInput';
import { SimpleInput } from './SimpleInput';
import { useStyles } from './styles';
Expand All @@ -14,6 +15,8 @@ import { useFormInputsState } from './useFormInputsState';
function getComponentForInput(input: InputProps, showErrors: boolean) {
const props = { ...input, error: showErrors ? input.error : undefined };
switch (input.typeDefinition.type) {
case InputType.Blob:
return <BlobInput {...props} />;
case InputType.Collection:
return <CollectionInput {...props} />;
case InputType.Map:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { makeStyles, Theme } from '@material-ui/core/styles';
import ErrorOutline from '@material-ui/icons/ErrorOutline';
import { NonIdealState } from 'components/common';
import { useCommonStyles } from 'components/common/styles';
import * as React from 'react';
import {
cannotLaunchWorkflowString,
requiredInputSuffix,
unsupportedRequiredInputsString
} from './constants';
import { ParsedInput } from './types';

const useStyles = makeStyles((theme: Theme) => ({
contentContainer: {
whiteSpace: 'pre-line',
textAlign: 'left'
},
errorContainer: {
marginBottom: theme.spacing(2)
}
}));

function formatLabel(label: string) {
return label.endsWith(requiredInputSuffix)
? label.substring(0, label.length - 1)
: label;
}

export interface UnsupportedRequiredInputsErrorProps {
inputs: ParsedInput[];
}
/** An informational error to be shown if a Workflow cannot be launch due to
* required inputs for which we will not be able to provide a value.
*/
export const UnsupportedRequiredInputsError: React.FC<UnsupportedRequiredInputsErrorProps> = ({
inputs
}) => {
const styles = useStyles();
const commonStyles = useCommonStyles();
return (
<NonIdealState
className={styles.errorContainer}
icon={ErrorOutline}
size="medium"
title={cannotLaunchWorkflowString}
>
<div className={styles.contentContainer}>
<p>{unsupportedRequiredInputsString}</p>
<ul className={commonStyles.listUnstyled}>
{inputs.map(input => (
<li
key={input.name}
className={commonStyles.textMonospace}
>
{formatLabel(input.label)}
</li>
))}
</ul>
</div>
</NonIdealState>
);
};
32 changes: 27 additions & 5 deletions src/components/Launch/LaunchWorkflowForm/__mocks__/mockInputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { dateToTimestamp, millisecondsToDuration } from 'common/utils';
import { Core } from 'flyteidl';
import { mapValues } from 'lodash';
import * as Long from 'long';
import { SimpleType, TypedInterface, Variable } from 'models/Common';
import {
BlobDimensionality,
SimpleType,
TypedInterface,
Variable
} from 'models/Common';
import { literalNone } from '../inputHelpers/constants';
import { primitiveLiteral } from './utils';

Expand All @@ -23,6 +28,7 @@ export type SimpleVariableKey =
| 'simpleInteger'
| 'simpleFloat'
| 'simpleBoolean'
| 'simpleBlob'
| 'simpleDuration'
| 'simpleDatetime'
| 'simpleBinary'
Expand All @@ -39,11 +45,14 @@ export const mockSimpleVariables: Record<SimpleVariableKey, Variable> = {
simpleDatetime: simpleType(SimpleType.DATETIME, 'a simple datetime value'),
simpleBinary: simpleType(SimpleType.BINARY, 'a simple binary value'),
simpleError: simpleType(SimpleType.ERROR, 'a simple error value'),
simpleStruct: simpleType(SimpleType.STRUCT, 'a simple struct value')
simpleStruct: simpleType(SimpleType.STRUCT, 'a simple struct value'),
simpleBlob: {
description: 'a simple single-dimensional blob',
type: { blob: { dimensionality: BlobDimensionality.SINGLE } }
}
// schema: {},
// collection: {},
// mapValue: {},
// blob: {}
// mapValue: {}
};

export const simpleVariableDefaults: Record<
Expand All @@ -63,7 +72,20 @@ export const simpleVariableDefaults: Record<
simpleError: literalNone(),
simpleFloat: primitiveLiteral({ floatValue: 1.5 }),
simpleInteger: primitiveLiteral({ integer: Long.fromNumber(12345) }),
simpleStruct: literalNone()
simpleStruct: literalNone(),
simpleBlob: {
scalar: {
blob: {
uri: 's3://someBlobUri/goesHere',
metadata: {
type: {
format: 'csv',
dimensionality: BlobDimensionality.SINGLE
}
}
}
}
}
};

export const mockCollectionVariables: Record<string, Variable> = mapValues(
Expand Down
17 changes: 17 additions & 0 deletions src/components/Launch/LaunchWorkflowForm/__mocks__/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
import { Core } from 'flyteidl';
import { BlobDimensionality } from 'models';

export function primitiveLiteral(primitive: Core.IPrimitive): Core.ILiteral {
return { scalar: { primitive } };
}

export function blobLiteral({
uri,
format,
dimensionality
}: {
uri?: string;
format?: string;
dimensionality?: BlobDimensionality;
}): Core.ILiteral {
return {
scalar: {
blob: { uri, metadata: { type: { format, dimensionality } } }
}
};
}
Loading

0 comments on commit 65f2a81

Please sign in to comment.