Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support custom serialization / deserialization (WIP) #220

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

mnahkies
Copy link
Owner

@mnahkies mnahkies commented Jun 8, 2024

(#225 solved the bug originally motivating this, but intend to experiment with ways to allow custom serialization / de-serialization in general on this branch)

  • introduce x-alpha-transform concept, allowing for a arbitrary transformation function to be applied to a schema
    • not yet supported by joi
    • not yet supported by type-builder
    • only really useful for this specific case so far
  • detect query parameters of an array type, and parse as a T | T[], and then transform to a T[] for convenience
    • does not yet support $refd schemas properly

relates #217

exclude: z
.union([z.array(z.enum(["repositories"])), z.enum(["repositories"])])
.optional()
.transform((it) => (Array.isArray(it) || it === undefined ? it : [it])),
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be nice to make this function name-able / reusable

@@ -248,6 +248,9 @@ export interface Schema {
externalDocs?: ExternalDocumentation | undefined
deprecated?: boolean | undefined
// xml?: XML | undefined

// TODO: not yet supported by type-builder or joi
"x-alpha-transform"?: string | undefined
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would probably be more useful to have this be shaped like {fn: string, type: string} such that we can plumb the return type through the type-builder properly

schema: string,
transformation: string | ((it: unknown) => unknown),
) {
// TODO: is it possible to do arbitrary transformations with `joi`?
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the way to do this with joi would be to register the transformation as an extension like shown in hapijs/joi#2796 (comment)

This will pretty much require that we make the functions nameable / reusable as we wouldn't want to register an extension for the same thing over and over.

@mnahkies mnahkies force-pushed the mn/217/fix-query-param-arrays branch from 2c3d2aa to 87dde06 Compare June 8, 2024 11:09
mnahkies added 3 commits June 21, 2024 10:28
- introduce `x-alpha-transform` concept, allowing for a arbitrary
  transformation function to be applied to a schema
  - not yet supported by `joi`
  - not yet supported by `type-builder`
  - only really useful for this specific case so far
- detect query parameters of an array type, and parse as a `T | T[]`,
  and then transform to a `T[]` for convenience
  - does not yet support `$ref`d schemas properly

relates #217
@mnahkies mnahkies force-pushed the mn/217/fix-query-param-arrays branch from 87dde06 to 71e466a Compare June 21, 2024 09:29
@@ -12,7 +12,7 @@ export type t_CreateUpdateTodoList = {
}

export type t_TodoList = {
created: string
created: Date
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be problematic without --enable-runtime-response-validation enabled, as by default we don't use runtime parsing in the client templates

schema: string,
transformation: string | ((it: unknown) => unknown),
) {
return [schema, `transform(${transformation.toString()})`]
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably actually need to use a preprocess here to get the desired effect, eg:

> z.preprocess(it => Array.isArray(it) ? it : [it], z.array(z.number())).parse([1,2])
[ 1, 2 ]
> z.preprocess(it => Array.isArray(it) ? it : [it], z.array(z.number())).parse(1)
[ 1 ]

@mnahkies mnahkies changed the title fix: support array of 1 for query parameter arrays feat: support custom serialization / deserialization (WIP) Jul 27, 2024
mnahkies added a commit that referenced this pull request Jul 27, 2024
#220 got a bit off-track with scope creep.

this PR takes a similar approach, but for internal use only
- detects query parameters with array type schemas
- adds an internal annotation to indicate these need preprocessing
- applies a `z.preprocess` to coerce an individual value to an array of
1 element, before parsing with the array schema
- for `joi` we resort to a pretty hacky wrapping of the schema with a
object that does the preprocessing, as the `joi` extension API didn't
work as I'd expected it to.

will continue experimenting with making this a more generally useful
feature for handling things like parsing `date-time` strings to be a
`Date`, etc, separately.

fixes #217 

**Testing Notes**
Need to improve automated test coverage still, but manually tested on a
running server:
- No query params
- 1 element
- 2 elements
- 2 elements, 1 element

and it seems to be working correctly.

```
listening on http://127.0.0.1:3000
query { query: {} }
query { query: { statuses: [ 'complete' ] } }
query { query: { statuses: [ 'complete', 'incomplete' ] } }
query { query: { statuses: [ 'complete', 'incomplete' ] } }
query { query: { statuses: [ 'complete', 'incomplete' ], tags: [ '123' ] } }
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant