Skip to content

Advice on integrating with CustomConstraint to replicate discriminated union validation #1186

@timocov

Description

@timocov

Hi there,

Sorry for bothering you with asking an advice on very specific subjects, but you're much more experienced with this than I am with you 🙂

I have a custom codegen integration that overrides serde for "discriminated" unions.

The way it is currently implemented is that for the deserialization I create a custom deserializer that first deserializes the content into document, checks the value of "discriminator" and calls document.deserializeInto(inputBuilder) based on the value.

For the serialization, I override serializeMembers of each discriminated union member so that instead of calling serializer.writeStruct($SCHEMA, first) it calls

serializer.writeString(CUSTOM_CREATED_SCHEMA_FOR_DISCRIMINATOR_FIELD, "<discriminator value>");
value.serializeMembers(serializer);

this kinda flattens the structure and adds "discriminator" into the result object.

So far it was working well and solved the problem, at least at the serde level.

But now I'm trying to integrate Validator into the pipeline and this is where it gets tricky 😂 If I just run the validator on an object with discriminated union in it, one of the errors I get back is Union member conflicts with from ValidatorOfUnion, which makes sense and generally is fine as I can filter it out before returning back. But another side-effect of this is that this error prevents from validating any other fields in this object (which again makes perfect sense from the validator point of view due to lack of knowledge about extra "features").

So I was thinking that with #1171 landed (I know its not yet released) to smithy-java it should be possible to "replicate" ValidatorOfUnion that would support such discrimiated unions - I could ignore error from original ValidatorOfUnion but run my own CustomConstraint that would handle it properly. But the problem I'm facing right now is that the way ValidatorOfUnion is implemented - it re-uses Validator.ShapeValidator (it takes it in as an ctor parameter) to run validation on downstream objects, and this class is not exposed to public (and I think I understand why).

In theory, in a custom constraint I can create another instance of Validator and run it against the discriminated unions only, but this can cause a couple of issues:

  • parameters of Validator (such as maxDepth or maxAllowedErrors) won't be synced
  • more importantly, currentPath, currentDepth and other validator run's "state" won't be shared, meaning that some of them will need to be re-constructed (e.g. path), but some couldn't (depth or current errors to prevent further validation if it reached maxAllowedErrors)

I was also thinking to play around with Schemas (e.g. create "merged" schemas for each member of a discriminated union so it contains union target + discriminator and then use it overridden schema method), but this approach feels like less extendable and controlled from the codegen perspective (e.g. there is no an easy way to add custom shapes to the model so that the codegen would generate schemas for them because it removes unused shapes from the model before running the codegen - otherwise I would need to replicate and maintain some code from smithy-java which I would rather to avoid doing; also there is no an easy way to override schema method either).

So I'm running out of ideas and I would appreciate any advice that you might have. Currently I'd prefer not to have discriminated union as a feature for many reasons, but this is something that is being used in existing APIs that we want to migrate over to Smithy before we can think about migrating them to a better state.

Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions