mulesoft-labs / raml-for-jax-rs

This project is all about two way transformation of JAX-RS-annotated Java code to RAML API description and back.
Other
296 stars 181 forks source link

"nil" is not supported #414

Open Kaiser1989 opened 4 years ago

Kaiser1989 commented 4 years ago

Seems that "nil" is not supported by ramltojaxrs:

Simple example:

Dog:
  type: object
  properties:
    bark: nil | boolean
    color: string

throws exception: org.raml.ramltopojo.GenerationException: can't fetch type named nil | boolean

I'm using dependency "org.raml.jaxrs:raml-to-jaxrs-gradle-plugin:3.0.6"

jpbelang commented 4 years ago

Looking at it. Sort of remember fixing something like that.

jpbelang commented 4 years ago

Can you check this ? https://repository-master.mulesoft.org/snapshots/org/raml/jaxrs/raml-to-jaxrs-gradle-plugin/3.0.8-SNAPSHOT ?

(We've been having problems with maven central and gradle).

Kaiser1989 commented 4 years ago

Thanks for your reply,

I tried your 3.0.8 SNAPSHOT, but still getting

Caused by: org.raml.ramltopojo.GenerationException: can't fetch type named nil | boolean

jpbelang commented 4 years ago

That's weird: I've got stuff generated appropriately (I think) in raml-for-jax-rs/raml-to-jaxrs/examples/maven-examples/type-torture-test/src/main/resources/world-music-api/api.raml

Can you share a pointer to your project of a bit of a larger sample ?

types:
  NilUnion:
    type: object
    properties:
      bark: nil | boolean
      color: string

Generating (and it's interface):

public class NilUnionImpl implements NilUnion {
  private NilUnion.BarkType bark;

  private String color;

  private Map<String, Object> additionalProperties = new ExcludingMap();

  public NilUnion.BarkType getBark() {
    return this.bark;
  }

  public void setBark(NilUnion.BarkType bark) {
    this.bark = bark;
  }

  public String getColor() {
    return this.color;
  }

  public void setColor(String color) {
    this.color = color;
  }

  public Map<String, Object> getAdditionalProperties() {
    return additionalProperties;
  }

  public void setAdditionalProperties(String key, Object value) {
    this.additionalProperties.put(key, value);
  }

  @Override
  public boolean equals(Object o) {
    if (o == null) return false;
    if (this == o) return true;
    if (o instanceof NilUnion) return false;
    NilUnionImpl other = (NilUnionImpl) o;
    return java.util.Objects.equals(this.bark, other.bark) && java.util.Objects.equals(this.color, other.color) && java.util.Objects.equals(this.additionalProperties, other.additionalProperties);
  }

  @Override
  public int hashCode() {
    return Objects.hash(bark,color,additionalProperties);
  }

  public static class BarkTypeImpl implements NilUnion.BarkType {
    private Object anyType;

    public BarkTypeImpl() {
      this.anyType = null;
    }

    public BarkTypeImpl(Boolean nilBoolean) {
      this.anyType = nilBoolean;
    }

    public Object getNil() {
      if ( !(anyType == null)) throw new IllegalStateException("fetching wrong type out of the union: NullType should be null");
      return null;
    }

    public boolean isNil() {
      return anyType == null;
    }

    public Boolean getBoolean() {
      if ( !(anyType instanceof  Boolean)) throw new IllegalStateException("fetching wrong type out of the union: java.lang.Boolean");
      return (Boolean) anyType;
    }

    public boolean isBoolean() {
      return anyType instanceof Boolean;
    }

    @Override
    public boolean equals(Object o) {
      if (o == null) return false;
      if (this == o) return true;
      if (o instanceof NilUnion.BarkType) return false;
      BarkTypeImpl other = (BarkTypeImpl) o;
      return java.util.Objects.equals(this.anyType, other.anyType);
    }

    @Override
    public int hashCode() {
      return Objects.hash(anyType);
    }
  }
}
Kaiser1989 commented 4 years ago

Thanks for your interest. There is no need for a larger example:

I created a minimal example of the problem: https://github.com/Kaiser1989/simpleRamlTest

I also found the evil guy, making it fail: <generateTypesWith><value>jackson</value></generateTypesWith> OR <generateTypesWith><value>jackson2</value></generateTypesWith>

Jackson and Jackson2 let it fail. If i remove them, it's building fine.

Do you have any idea why this is not working with jackson?

Kaiser1989 commented 4 years ago

I finally found a workaround for this: I don't use nil values anymore. I added a custom Plugin to provide java.util.Optional support:

With my OptionalPlugin:

...

@Override
public TypeName typeName(ReferencePluginContext referencePluginContext, TypeDeclaration ramlType, TypeName currentSuggestion) { 
    return ParameterizedTypeName.get(ClassName.get(Optional.class), currentSuggestion.box());
}

...

And then in my raml i use types annotation:

  DtoTest:
    type: object
    properties:
      foo:
        type: string
        (types):
          plugins:
            - name: pp.optionalType

Now i have a valid distinction between not exisiting, required but nullable, and normal values.

jpbelang commented 4 years ago

That's awesome. I'll look into the original issue this week, and maybe try to integrate your Optional/Nil idea into normal spec parsing.

Thanks very much for your help....

Kaiser1989 commented 4 years ago

Thanks for your feedback. I like your project and will help whenever I can.

Of course it would be nice, if the "nil" stuff would work together with jackson. The annotation thing is just a functional workaround and makes the raml become implementation dependend.

It's working, but it's not specification compatible.

Best regards