forked from flyteorg/flyte
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support union type for launch plan (flyteorg#540)
* feat: support union type for launch plan Signed-off-by: eugenejahn <[email protected]> * fix: format Signed-off-by: eugenejahn <[email protected]> * fix: update type label Signed-off-by: eugenejahn <[email protected]> * fix: update the format Signed-off-by: eugenejahn <[email protected]>
- Loading branch information
1 parent
7e44dca
commit f6f8283
Showing
10 changed files
with
270 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
157 changes: 157 additions & 0 deletions
157
packages/zapp/console/src/components/Launch/LaunchForm/UnionInput.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
import { Typography } from '@material-ui/core'; | ||
import { makeStyles, Theme } from '@material-ui/core/styles'; | ||
import * as React from 'react'; | ||
import Card from '@material-ui/core/Card'; | ||
import CardContent from '@material-ui/core/CardContent'; | ||
import { InputProps, InputType, InputTypeDefinition, UnionValue, InputValue } from './types'; | ||
import { formatType } from './utils'; | ||
import { getComponentForInput } from './LaunchFormInputs'; | ||
import { getHelperForInput } from './inputHelpers/getHelperForInput'; | ||
import { SearchableSelector, SearchableSelectorOption } from './SearchableSelector'; | ||
import t from '../../common/strings'; | ||
|
||
const useStyles = makeStyles((theme: Theme) => ({ | ||
inlineTitle: { | ||
display: 'flex', | ||
gap: theme.spacing(1), | ||
alignItems: 'center', | ||
paddingBottom: theme.spacing(3), | ||
}, | ||
})); | ||
|
||
const generateInputTypeToValueMap = ( | ||
listOfSubTypes: InputTypeDefinition[] | undefined, | ||
initialInputValue: UnionValue | undefined, | ||
initialType: InputTypeDefinition, | ||
): Record<InputType, InputValue> | {} => { | ||
if (!listOfSubTypes?.length) { | ||
return {}; | ||
} | ||
|
||
return listOfSubTypes.reduce(function (map, subType) { | ||
if (initialInputValue && subType === initialType) { | ||
map[subType.type] = initialInputValue; | ||
} else { | ||
map[subType.type] = { value: '', typeDefinition: subType }; | ||
} | ||
return map; | ||
}, {}); | ||
}; | ||
|
||
const generateSearchableSelectorOption = ( | ||
inputTypeDefinition: InputTypeDefinition, | ||
): SearchableSelectorOption<InputType> => { | ||
return { | ||
id: inputTypeDefinition.type, | ||
data: inputTypeDefinition.type, | ||
name: formatType(inputTypeDefinition), | ||
} as SearchableSelectorOption<InputType>; | ||
}; | ||
|
||
const generateListOfSearchableSelectorOptions = ( | ||
listOfInputTypeDefinition: InputTypeDefinition[], | ||
): SearchableSelectorOption<InputType>[] => { | ||
return listOfInputTypeDefinition.map((inputTypeDefinition) => | ||
generateSearchableSelectorOption(inputTypeDefinition), | ||
); | ||
}; | ||
|
||
export const UnionInput = (props: InputProps) => { | ||
const { initialValue, required, label, onChange, typeDefinition, error, description } = props; | ||
|
||
const classes = useStyles(); | ||
|
||
const listOfSubTypes = typeDefinition?.listOfSubTypes; | ||
|
||
if (!listOfSubTypes?.length) { | ||
return <></>; | ||
} | ||
|
||
const inputTypeToInputTypeDefinition = listOfSubTypes.reduce( | ||
(previous, current) => ({ ...previous, [current.type]: current }), | ||
{}, | ||
); | ||
|
||
const initialInputValue = | ||
initialValue && | ||
(getHelperForInput(typeDefinition.type).fromLiteral( | ||
initialValue, | ||
typeDefinition, | ||
) as UnionValue); | ||
|
||
const initialInputTypeDefinition = initialInputValue?.typeDefinition ?? listOfSubTypes[0]; | ||
|
||
if (!initialInputTypeDefinition) { | ||
return <></>; | ||
} | ||
|
||
const [inputTypeToValueMap, setInputTypeToValueMap] = React.useState< | ||
Record<InputType, InputValue> | {} | ||
>(generateInputTypeToValueMap(listOfSubTypes, initialInputValue, initialInputTypeDefinition)); | ||
|
||
const [selectedInputType, setSelectedInputType] = React.useState<InputType>( | ||
initialInputTypeDefinition.type, | ||
); | ||
|
||
const selectedInputTypeDefintion = inputTypeToInputTypeDefinition[ | ||
selectedInputType | ||
] as InputTypeDefinition; | ||
|
||
const handleTypeOnSelectionChanged = (value: SearchableSelectorOption<InputType>) => { | ||
setSelectedInputType(value.data); | ||
}; | ||
|
||
const handleSubTypeOnChange = (input: InputValue) => { | ||
onChange({ | ||
value: input, | ||
typeDefinition: selectedInputTypeDefintion, | ||
} as UnionValue); | ||
setInputTypeToValueMap({ | ||
...inputTypeToValueMap, | ||
[selectedInputType]: { | ||
value: input, | ||
typeDefinition: selectedInputTypeDefintion, | ||
} as UnionValue, | ||
}); | ||
}; | ||
|
||
return ( | ||
<Card | ||
variant="outlined" | ||
style={{ | ||
overflow: 'visible', | ||
}} | ||
> | ||
<CardContent> | ||
<div className={classes.inlineTitle}> | ||
<Typography variant="body1" component="label"> | ||
{label} | ||
</Typography> | ||
|
||
<SearchableSelector | ||
label={t('type')} | ||
options={generateListOfSearchableSelectorOptions(listOfSubTypes)} | ||
selectedItem={generateSearchableSelectorOption(selectedInputTypeDefintion)} | ||
onSelectionChanged={handleTypeOnSelectionChanged} | ||
/> | ||
</div> | ||
|
||
<div> | ||
{getComponentForInput( | ||
{ | ||
description: description, | ||
name: `${formatType(selectedInputTypeDefintion)}`, | ||
label: '', | ||
required: required, | ||
typeDefinition: selectedInputTypeDefintion, | ||
onChange: handleSubTypeOnChange, | ||
value: inputTypeToValueMap[selectedInputType]?.value, | ||
error: error, | ||
} as InputProps, | ||
true, | ||
)} | ||
</div> | ||
</CardContent> | ||
</Card> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
65 changes: 65 additions & 0 deletions
65
packages/zapp/console/src/components/Launch/LaunchForm/inputHelpers/union.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { Core } from 'flyteidl'; | ||
import { isObject } from 'lodash'; | ||
import { InputTypeDefinition, InputValue, UnionValue } from '../types'; | ||
import { getHelperForInput } from './getHelperForInput'; | ||
import { ConverterInput, InputHelper, InputValidatorParams } from './types'; | ||
import t from '../../../common/strings'; | ||
|
||
function fromLiteral(literal: Core.ILiteral, inputTypeDefinition: InputTypeDefinition): InputValue { | ||
const { listOfSubTypes } = inputTypeDefinition; | ||
if (!listOfSubTypes?.length) { | ||
throw new Error(t('missingUnionListOfSubType')); | ||
} | ||
|
||
// loop though the subtypes to find the correct match literal type | ||
for (let i = 0; i < listOfSubTypes.length; i++) { | ||
try { | ||
const value = getHelperForInput(listOfSubTypes[i].type).fromLiteral( | ||
literal, | ||
listOfSubTypes[i], | ||
); | ||
return { value, typeDefinition: listOfSubTypes[i] } as UnionValue; | ||
} catch (error) { | ||
// do nothing here. it's expected to have error from fromLiteral | ||
// because we loop through all the type to decode the input value | ||
// the error should be something like this | ||
// new Error(`Failed to extract literal value with path ${path}`); | ||
} | ||
} | ||
throw new Error(t('noMatchingResults')); | ||
} | ||
|
||
function toLiteral({ value, typeDefinition: { listOfSubTypes } }: ConverterInput): Core.ILiteral { | ||
if (!listOfSubTypes) { | ||
throw new Error(t('missingUnionListOfSubType')); | ||
} | ||
|
||
if (!isObject(value)) { | ||
throw new Error(t('valueMustBeObject')); | ||
} | ||
|
||
const { value: unionValue, typeDefinition } = value as UnionValue; | ||
|
||
return getHelperForInput(typeDefinition.type).toLiteral({ | ||
value: unionValue, | ||
typeDefinition: typeDefinition, | ||
} as ConverterInput); | ||
} | ||
|
||
function validate({ value, typeDefinition: { listOfSubTypes } }: InputValidatorParams) { | ||
if (!value) { | ||
throw new Error(t('valueRequired')); | ||
} | ||
if (!isObject(value)) { | ||
throw new Error(t('valueMustBeObject')); | ||
} | ||
|
||
const { typeDefinition } = value as UnionValue; | ||
getHelperForInput(typeDefinition.type).validate(value as InputValidatorParams); | ||
} | ||
|
||
export const unionHelper: InputHelper = { | ||
fromLiteral, | ||
toLiteral, | ||
validate, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.