OpenAPITools / OpenAPITools/openapi-generator

[BUG][Java][Client] Multiple oneOf => Multiple same discriminator

Open
#5,852 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Issue: Bug
Dominant language
Java
Stars
26.8k
Forks
7.7k
PR merge metrics
PR metrics pending

Description

Bug Report Checklist
  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator (example)?
  • What's the version of OpenAPI Generator used?
  • Have you search for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Bounty to sponsor the fix (example)
Description

When generating java feign client from yaml where there is same object in multiple oneOf, the generator produce duplicated discriminator attributes.

openapi-generator version

OpenApi Generator version : 4.3.0 with OpenAPI V3 file

OpenAPI declaration file content or url

gist : https://gist.github.com/LeComptoirDesPharmacies/f28c0a189cf6a1312b8c1d5d7b59c0f7

Command line used for generation

java -cp openapi-generator-cli-4.3.0.jar org.openapitools.codegen.OpenAPIGenerator generate -g java -i example.yaml -o output --library feign

Steps to reproduce

Launch the command. You will see that ModelB is generated like the following with multiple distributionType fields :

/**
 * ModeB
 */
@JsonPropertyOrder({
  ModeB.JSON_PROPERTY_ANY_PROP_C,
  ModeB.JSON_PROPERTY_DISTRIBUTION_TYPE,
  ModeB.JSON_PROPERTY_DISTRIBUTION_TYPE
})
@javax.annotation.Generated(value = "fr.lcdp.codegen.JavaPlayFeignClientCodegen", date = "2020-04-07T22:25:50.963278800+02:00[Europe/Paris]")
public class ModeB extends Mode implements AnyIdentifiedMode, AnyMode {
  public static final String JSON_PROPERTY_ANY_PROP_C = "anyPropC";
  private String anyPropC;

  public static final String JSON_PROPERTY_DISTRIBUTION_TYPE = "distributionType";
  private String distributionType;

  public static final String JSON_PROPERTY_DISTRIBUTION_TYPE = "distributionType";
  private String distributionType;

Expected value should be :

/**
 * ModeB
 */
@JsonPropertyOrder({
  ModeB.JSON_PROPERTY_ANY_PROP_C,
  ModeB.JSON_PROPERTY_DISTRIBUTION_TYPE
})
@javax.annotation.Generated(value = "fr.lcdp.codegen.JavaPlayFeignClientCodegen", date = "2020-04-07T22:25:50.963278800+02:00[Europe/Paris]")
public class ModeB extends Mode implements AnyIdentifiedMode, AnyMode {
  public static final String JSON_PROPERTY_ANY_PROP_C = "anyPropC";
  private String anyPropC;

  public static final String JSON_PROPERTY_DISTRIBUTION_TYPE = "distributionType";
  private String distributionType;
Suggest a fix

I think that it can came from this new lines which manage oneOf aspect of OpenAPI v3. Generator add to current models properties from implemented interfaces (oneOf) event if they are already in additionalProps.
https://github.com/OpenAPITools/openapi-generator/pull/4785/files#diff-df91e7fc730a53c408e0e3b2f22ab86eR961

Temporary workaroung

We created a wrapping generator of JavaClientCodegen with the following method to clean the vars after postProcess :

  public static <T> Predicate<T> distinctByKey(
    Function<? super T, ?> keyExtractor) {

    Map<Object, Boolean> seen = new ConcurrentHashMap<>();
    return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
  }

  @Override
  public Map<String, Object> postProcessAllModels(Map<String, Object> objs) {
    objs = super.postProcessAllModels(objs);

    if (this.useOneOfInterfaces) {
      for (Map.Entry modelsEntry : objs.entrySet()) {
        Map<String, Object> modelsAttrs = (Map<String, Object>) modelsEntry.getValue();
        List<Object> models = (List<Object>) modelsAttrs.get("models");
        for (Object _mo : models) {
          Map<String, Object> mo = (Map<String, Object>) _mo;
          CodegenModel cm = (CodegenModel) mo.get("model");
          if (cm.getVendorExtensions().containsKey("implements"))
          {
            if (cm.vars.size() > 0) {
              // Filter duplicate
              cm.vars = cm.vars.stream()
                      .filter(distinctByKey(codegenProperty -> codegenProperty.baseName))
                      .collect(Collectors.toList());

              // Re-compute hasMore flag
              CodegenProperty last = cm.vars.remove(cm.vars.size() - 1);
              last.hasMore = false;
              cm.vars.stream().map(codegenProperty -> codegenProperty.hasMore=true);
              cm.vars.add(last);
              cm.hasVars = true;
            }
          }
        }
      }
    }

    return objs;
  }

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in the Java client codegen model-processing path, especially postProcessAllModels and the oneOf changes referenced in PR 4785. Reproduce with the linked gist and the Java Feign command, then verify that a model appearing in multiple oneOf interfaces has only one distributionType field and discriminator entry.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, openapi
Domain
api, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.