Skip to content
Permalink
Browse files

[CLOSES #933] Add enum support to web-actions tab form (#1220)

  • Loading branch information...
adrw committed Oct 9, 2019
1 parent dc61f1c commit 3f28caf9fc3b2f47c3f0c0cdf734aa224b0cfb56
@@ -93,15 +93,34 @@ class RequestTypes {
// TODO: Support maps
fields.add(Field(fieldName, fieldClass.qualifiedName!!, repeated))
}
Enum::class -> {
handleEnumField(fieldClass, fields, fieldName, repeated)
}
else -> {
if (fieldClass.superclasses.contains(WireEnum::class)) {
// TODO: Communicate back enum values
fields.add(Field(fieldName, fieldClass.qualifiedName!!, repeated))
handleEnumField(fieldClass, fields, fieldName, repeated)
} else {
fields.add(Field(fieldName, fieldClass.qualifiedName!!, repeated))
stack.push(fieldClass)
}
}
}
}

/**
* Adds a field with a type that has the class name and enum values embedded
* Example: "Enum<app.cash.backfila.BackfillType,ISOLATED,PARALLEL>"
*/
private fun handleEnumField(
fieldClass: KClass<*>,
fields: MutableList<Field>,
fieldName: String,
repeated: Boolean
) {
val enumValues =
(fieldClass.members.find { it.name == "values" }?.call() as Array<*>).map { (it as Enum<*>).name }
fields.add(
Field(fieldName, "Enum<${fieldClass.qualifiedName!!},${enumValues.joinToString(",")}>",
repeated))
}
}
@@ -37,5 +37,8 @@ internal class RequestTypesTest {
// Check repeated types
assertThat(shipmentType.fields).contains(Field("notes", "String", true))
assertThat(warehouseType.fields).contains(Field("alternates", Warehouse::class.qualifiedName!!, true))

// Check enum types
assertThat(shipmentType.fields).contains(Field("status", "Enum<com.squareup.protos.test.parsing.Shipment.State,VALIDATING,PICKING_UP,DELIVERING,CONSUMING>", false))
}
}
@@ -5,6 +5,7 @@ import {
Card,
ControlGroup,
FormGroup,
HTMLSelect,
InputGroup,
Intent,
Label,
@@ -38,7 +39,9 @@ import {
mapDispatchToProps,
mapStateToProps,
padId,
TypescriptBaseTypes
parseEnumType,
TypescriptBaseTypes,
ServerTypes
} from "../ducks"

const cssCard = css`
@@ -178,9 +181,11 @@ const EditRawInput = (
}

const formLabelFormatter = (name: string, serverType: string) => {
if (name && serverType) {
const st = serverType.split(".").join(" ")
return <WrapTextContainer>{`${name} (${st})`}</WrapTextContainer>
if (name && serverType && serverType.startsWith(ServerTypes.Enum)) {
const { enumClassName } = parseEnumType(serverType)
return <WrapTextContainer>{`${name} (${enumClassName})`}</WrapTextContainer>
} else if (name && serverType) {
return <WrapTextContainer>{`${name} (${serverType})`}</WrapTextContainer>
} else {
return <span />
}
@@ -312,6 +317,34 @@ const UnconnectedRequestFormFieldBuilderContainer = (
</ControlGroup>
</FormGroup>
)
} else if (typescriptType === TypescriptBaseTypes.enum) {
const { enumValues } = parseEnumType(serverType)
return (
<FormGroup
css={css(cssFormGroup)}
label={formLabelFormatter(name, serverType)}
>
<ControlGroup>
{...repeatableFieldButtons({ ...props, id })}
<EditRawInput {...props} id={id} tag={tag}>
<HTMLSelect
large={true}
onChange={onChangeFnCall(
props.simpleMergeData,
`${tag}::${padId(id)}`
)}
onClick={onChangeFnCall(clickDirtyInputFns(props))}
onBlur={onChangeFnCall(
props.simpleMergeData,
`${tag}::${padId(id)}`
)}
// Show empty option to prompt selection since first option is not automatically persisted
options={[""].concat(enumValues)}
/>
</EditRawInput>
</ControlGroup>
</FormGroup>
)
} else if (typescriptType === null && idChildren.size > 0) {
if (
idChildren.first() &&
@@ -39,7 +39,7 @@ export const enum ServerTypes {
"ByteString" = "ByteString",
"Char" = "Char",
"Double" = "Double",
// "Enum" = "String",
"Enum" = "Enum",
"Float" = "Float",
"Int" = "Int",
"JSON" = "JSON",
@@ -59,7 +59,8 @@ export const BaseFieldTypes: IBaseFieldTypes = {
[ServerTypes.JSON]: TypescriptBaseTypes.string,
[ServerTypes.Long]: TypescriptBaseTypes.number,
[ServerTypes.ByteString]: TypescriptBaseTypes.string,
[ServerTypes.String]: TypescriptBaseTypes.string
[ServerTypes.String]: TypescriptBaseTypes.string,
[ServerTypes.Enum]: TypescriptBaseTypes.enum
}

export interface IFieldTypeMetadata {
@@ -377,6 +378,26 @@ export const parseType = (
}
}

/**
* Parses for Enum server type
* Expects type to match format
* "Enum<qualified.class.name,enumValue1,enumValue2>"
*/
export interface IParseEnumType {
enumClassName: string
enumValues: string[]
}

export const parseEnumType = (serverType: string): IParseEnumType => {
const enumType = serverType
.split("<")[1]
.split(">")[0]
.split(",")
const enumClassName = enumType[0]
const enumValues = enumType.slice(1)
return { enumClassName, enumValues }
}

const isInput = (data: any) =>
isBoolean(data) ||
(isObject(data) && size(data) > 0) ||
@@ -717,7 +738,11 @@ const generateFieldTypesMetadata = (
id
)
)
} else if (BaseFieldTypes.hasOwnProperty(type)) {
} else if (
BaseFieldTypes.hasOwnProperty(type) ||
// Check if it is a complex type such as Enum<className,value1,value2>
BaseFieldTypes.hasOwnProperty(type.split("<")[0])
) {
if (
BaseFieldTypes[type] === TypescriptBaseTypes.boolean ||
BaseFieldTypes[type] === TypescriptBaseTypes.number ||
@@ -737,6 +762,24 @@ const generateFieldTypesMetadata = (
)
)
)
} else if (
// Handle enum type ie. Enum<className,value1,value2>
BaseFieldTypes[type.split("<")[0]] === TypescriptBaseTypes.enum
) {
return typesMetadata.mergeDeep(
OrderedMap<string, ITypesFieldMetadata>().set(
id,
buildTypeFieldMetadata(
OrderedSet(),
id,
name,
repeated,
parent,
type,
TypescriptBaseTypes.enum
)
)
)
} else {
console.error(
`Web Action request body field ${field} with type ${type} has no handler for the corresponding Tyepscript Type ${BaseFieldTypes[type]}`
@@ -0,0 +1,13 @@
import { parseEnumType } from "../../src/ducks"

describe("Parse server type Enum", () => {
const testEnumType = "Enum<app.cash.common.AlphaEnum,Alpha,Bravo,Delta>"
it("parses class name", () => {
const parsed = parseEnumType(testEnumType)
expect(parsed.enumClassName).toEqual("app.cash.common.AlphaEnum")
})
it("parses enum values", () => {
const parsed = parseEnumType(testEnumType)
expect(parsed.enumValues).toEqual(["Alpha", "Bravo", "Delta"])
})
})

0 comments on commit 3f28caf

Please sign in to comment.
You can’t perform that action at this time.