blackmann/mangobase

Add enum support for `string` type

Closed this issue · 5 comments

Sometimes we'll want some string values to match from a set of options (enums). The schema and admin UI should support setting an optional enum field for string types.

interface StringDefinition {
  type: 'string'
  treatAs?: 'email' | 'url' | 'code'
+ enum?: string[]
  defaultValue?: string
}

Additional context:

  • schema: when an enum property is defined for a field of type string,
    • during data validation, the value passed should be one of the enum values
    • during schema validation, the enum field should not be an empty array.
  • UI: Provide a chip input to accept enum values. The chip input should be below the required/unique fields. It's not required to provide values. A chip input component already exists.

Files to look at

https://github.com/blackmann/mangobase/blob/d53a317831c0db5799ce7c54b7805ba4cd721817/base/src/schema.ts
https://github.com/blackmann/mangobase/blob/2bfae81de382b918943db2e04380bae3f8344faf/admin/src/components/field.tsx

Tests

Add some unit tests, at least for

  • data validation
  • schema validation

https://github.com/blackmann/mangobase/blob/d53a317831c0db5799ce7c54b7805ba4cd721817/base/src/schema.test.ts

❇️ The codebase is structured to allow making these changes very easy. You'll mostly have to reference previous code.

Would it not be an acceptable approach to define enums as a Type of their own?
Most languages Typescript, Java type systems define an Enum type also; and using this approach of declaring Enums as a Type will make things clearer and eliminate accidental complexities.

Can you elaborate what "type of their own" will look like in terms of schema, database and UI representation?

Also can you mention some "accidental complexities" that come to mind?

@frankchoongsaeng

Can you elaborate what "type of their own" will look like in terms of schema, database and UI representation?

This implies creating a TypeDefinition for Enum like

interface EnumDefinition {
  type: 'string' // not sure what this type is used for so I've left it as a string, but this should be 'enum'
  values: string[]
  defaultValue?: string
}

Basically baking the concept of enums into your type definitions. They don't change the way you store up values in your database, etc, but these concepts could make your life easier in terms of implementing new features.

Also can you mention some "accidental complexities" that come to mind?

Validation would be a great example. What happens when you have conflicting requirements for a field?
Like say, for some reason, you end up with a StringDefinition that has been marked with treatAs: email, and also has enums: ['foo', 'bar']?

While you might be able to chain a bunch of if-else's together to prevent something like that from happening, these are clear signs that these things don't belong together, and trying to fit them together just opens up possibilities of introducing errors with future changes.

Actually, about the DB part, there's something else you get from defining enums separately. You can have a table for each enum.

define schema MyEnum (
  id: Integer,
  value: String
)

This offers meaning to a relationship between datasets and these enum values, and also allows you to rename the values without changing the relationship that existing dataset has to these enums

Right.

Thanks for the suggestions.

But enum is just a validation helper here. That's all. Just like required is.

This is not a new concept and has implementations in the JSONschema/enum and Django/choices if you will.


treatAs is only an annotation currently used by the UI only and doesn't really care about the value.