phoenixnap / springmvc-raml-plugin

Spring MVC - RAML Spec Synchroniser Plugin. A Maven plugin designed to Generate Server & Client code in Spring from a RAML API descriptor and conversely, a RAML API document from the SpringMVC Server implementation.
Apache License 2.0
136 stars 84 forks source link

Union type support broken #289

Open Phil-Ba opened 5 years ago

Phil-Ba commented 5 years ago

Currently trying to use union types leads to a NullPointerException:

Caused by: java.lang.NullPointerException at com.phoenixnap.oss.ramlplugin.raml2code.interpreters.UnionTypeInterpreter.getParent(UnionTypeInterpreter.java:69) at com.phoenixnap.oss.ramlplugin.raml2code.interpreters.UnionTypeInterpreter.interpret(UnionTypeInterpreter.java:115) at com.phoenixnap.oss.ramlplugin.raml2code.helpers.RamlTypeHelper.mapTypeToPojo(RamlTypeHelper.java:100) at com.phoenixnap.oss.ramlplugin.raml2code.plugin.SpringMvcEndpointGeneratorMojo.generateUnreferencedObjects(Spring

Example:

types:
  boolOrString:
    displayName: test
    type: string | boolean
Phil-Ba commented 5 years ago

Ok, problem seems to occur when description is missing. This seems to be working

types:
  boolOrString:
    description: test
    type: string | boolean
stojsavljevic commented 5 years ago

Hi @Phil-Ba,

Thanks for reporting the issue. I'm getting the same exception with both examples if the type is used as a query parameter. Some other exceptions happen if the type is used as a request body.

TBH, I'm not sure how generated code should look like in this case. What would you expect to get? How should boolOrString POJO look like?

lctncld commented 5 years ago

I think there are at least two ways we can implement union types in Java. They are both not perfect but viable when type is used as a request or response body. However query parameter is a different story, I'm at loss there too.

So first approach is to create a field and an accessor for each type of the union. This is similar to how plugin works right now (see raml-with-union-types), but there are some problems when types extend other user-defined types (https://github.com/phoenixnap/springmvc-raml-plugin/pull/298).

public class BoolOrString {

  private Boolean booleanType;
  private String stringType;

  public Boolean getBooleanType() {
    return booleanType;
  }

  public String getStringType() {
    return stringType;
  }
}

Another approach, currently implemented in mulesoft-labs/raml-for-jax-rs, is to use a single field and provide accessors with type checking and casting. I haven't tried this myself and not sure deserialization would work.

public class BoolOrString {

  private Object anyType;

  public Boolean getBooleanType() {
    if (!(anyType instanceof Boolean)) throw new IllegalStateException();
    return (Boolean) anyType;
  }

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

  public String getStringType() {
    if (!(anyType instanceof String)) throw new IllegalStateException();
    return (String) anyType;
  }

  public boolean isStringType() {
    return anyType instanceof String;
  }

}
stojsavljevic commented 5 years ago

Let's go with the second approach - the one used in raml-for-jax-rs.