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

[BUG] Broken Java code generated from yaml specification #12884

Closed
6 tasks done
alfredomusumeci opened this issue Jul 15, 2022 · 3 comments
Closed
6 tasks done

[BUG] Broken Java code generated from yaml specification #12884

alfredomusumeci opened this issue Jul 15, 2022 · 3 comments

Comments

@alfredomusumeci
Copy link

Bug Report Checklist

  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator (example)?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

I am trying to generate Java code from an OpenAPI specification (parsing a .yaml file), and the produced result is broken code (i.e., missing commas, "&&" when doing comparisons, = followed by a semicolon straight after). A personalized MyCodegen is utilized as -g argument; the problem persists even when using "java".
The input file is a file whose specifications have been extracted from the following https://github.com/GoogleCloudPlatform/k8s-config-connector/blob/master/install-bundles/install-bundle-workload-identity/crds.yaml.

Examples of broken code:

  @JsonProperty("apiVersion")
  private String apiVersion = ;

  @JsonProperty("kind")
  private String kind = ;
  @Override
  public int hashCode() {
    return Objects.hash(apiVersionkindmetadataspecstatus);
  }

To clarify, correct format should be:

  @Override 
  public int hashCode() { 
    return Objects.hash(apiVersion, kind, metadata, spec, status, super.hashCode()); 
  } 

Wrong comparison method missing '&&':

  @Override
  public boolean equals(java.lang.Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    AccessContextManagerAccessLevel accessContextManagerAccessLevel = (AccessContextManagerAccessLevel) o;
    return Objects.equals(this.apiVersion, accessContextManagerAccessLevel.apiVersion)Objects.equals(this.kind, accessContextManagerAccessLevel.kind)Objects.equals(this.metadata, accessContextManagerAccessLevel.metadata)Objects.equals(this.spec, accessContextManagerAccessLevel.spec)Objects.equals(this.status, accessContextManagerAccessLevel.status);
  }
openapi-generator version

6.0.1

OpenAPI declaration file content or url

This is an extract of the used yaml file.

components:
  schemas:
    AccessContextManagerAccessLevel:
      properties:
        apiVersion:
          description: 'apiVersion defines the versioned schema of this representation
            of an object. Servers should convert recognized schemas to the latest
            internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources'
          type: string
        kind:
          description: 'kind is a string value representing the REST resource this
            object represents. Servers may infer this from the endpoint the client
            submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
          type: string
        metadata:
          type: V1ObjectMeta
        spec:
          properties:
            accessPolicyRef:
              description: |-
                The AccessContextManagerAccessPolicy this
                AccessContextManagerAccessLevel lives in.
              oneOf:
              - not:
                  required:
                  - external
                required:
                - name
              - not:
                  anyOf:
                  - required:
                    - name
                  - required:
                    - namespace
                required:
                - external
              properties:
                external:
                  description: 'Allowed value: string of the format `accessPolicies/{{value}}`,
                    where {{value}} is the `name` field of an `AccessContextManagerAccessPolicy`
                    resource.'
                  type: string
                name:
                  description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
                  type: string
                namespace:
                  description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
                  type: string
              type: object
            basic:
              description: A set of predefined conditions for the access level and
                a combining function.
              properties:
                combiningFunction:
                  description: |-
                    How the conditions list should be combined to determine if a request
                    is granted this AccessLevel. If AND is used, each Condition in
                    conditions must be satisfied for the AccessLevel to be applied. If
                    OR is used, at least one Condition in conditions must be satisfied
                    for the AccessLevel to be applied. Default value: "AND" Possible values: ["AND", "OR"].
                  type: string
                conditions:
                  description: A set of requirements for the AccessLevel to be granted.
                  items:
                    properties:
                      devicePolicy:
                        description: |-
                          Device specific restrictions, all restrictions must hold for
                          the Condition to be true. If not specified, all devices are
                          allowed.
                        properties:
                          allowedDeviceManagementLevels:
                            description: |-
                              A list of allowed device management levels.
                              An empty list allows all management levels. Possible values: ["MANAGEMENT_UNSPECIFIED", "NONE", "BASIC", "COMPLETE"].
                            items:
                              type: string
                            type: array
                          allowedEncryptionStatuses:
                            description: |-
                              A list of allowed encryptions statuses.
                              An empty list allows all statuses. Possible values: ["ENCRYPTION_UNSPECIFIED", "ENCRYPTION_UNSUPPORTED", "UNENCRYPTED", "ENCRYPTED"].
                            items:
                              type: string
                            type: array
                          osConstraints:
                            description: |-
                              A list of allowed OS versions.
                              An empty list allows all types and all versions.
                            items:
                              properties:
                                minimumVersion:
                                  description: |-
                                    The minimum allowed OS version. If not set, any version
                                    of this OS satisfies the constraint.
                                    Format: "major.minor.patch" such as "10.5.301", "9.2.1".
                                  type: string
                                osType:
                                  description: 'The operating system type of the device.
                                    Possible values: ["OS_UNSPECIFIED", "DESKTOP_MAC",
                                    "DESKTOP_WINDOWS", "DESKTOP_LINUX", "DESKTOP_CHROME_OS",
                                    "ANDROID", "IOS"].'
                                  type: string
                                requireVerifiedChromeOs:
                                  description: If you specify DESKTOP_CHROME_OS for
                                    osType, you can optionally include requireVerifiedChromeOs
                                    to require Chrome Verified Access.
                                  type: boolean
                              required:
                              - osType
                              type: object
                            type: array
                          requireAdminApproval:
                            description: Whether the device needs to be approved by
                              the customer admin.
                            type: boolean
                          requireCorpOwned:
                            description: Whether the device needs to be corp owned.
                            type: boolean
                          requireScreenLock:
                            description: |-
                              Whether or not screenlock is required for the DevicePolicy
                              to be true. Defaults to false.
                            type: boolean
                        type: object
                      ipSubnetworks:
                        description: |-
                          A list of CIDR block IP subnetwork specification. May be IPv4
                          or IPv6.
                          Note that for a CIDR IP address block, the specified IP address
                          portion must be properly truncated (i.e. all the host bits must
                          be zero) or the input is considered malformed. For example,
                          "192.0.2.0/24" is accepted but "192.0.2.1/24" is not. Similarly,
                          for IPv6, "2001:db8::/32" is accepted whereas "2001:db8::1/32"
                          is not. The originating IP of a request must be in one of the
                          listed subnets in order for this Condition to be true.
                          If empty, all IP addresses are allowed.
                        items:
                          type: string
                        type: array
                      members:
                        items:
                          description: |-
                            An allowed list of members (users, service accounts).
                            Using groups is not supported.

                            The signed-in user originating the request must be a part of one
                            of the provided members. If not specified, a request may come
                            from any user (logged in/not logged in, not present in any
                            groups, etc.).
                          oneOf:
                          - required:
                            - serviceAccountRef
                          - required:
                            - user
                          properties:
                            serviceAccountRef:
                              oneOf:
                              - not:
                                  required:
                                  - external
                                required:
                                - name
                              - not:
                                  anyOf:
                                  - required:
                                    - name
                                  - required:
                                    - namespace
                                required:
                                - external
                              properties:
                                external:
                                  description: 'Allowed value: string of the format
                                    `serviceAccount:{{value}}`, where {{value}} is
                                    the `email` field of an `IAMServiceAccount` resource.'
                                  type: string
                                name:
                                  description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
                                  type: string
                                namespace:
                                  description: 'Namespace of the referent. More info:
                                    https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
                                  type: string
                              type: object
                            user:
                              type: string
                          type: object
                        type: array
                      negate:
                        description: |-
                          Whether to negate the Condition. If true, the Condition becomes
                          a NAND over its non-empty fields, each field must be false for
                          the Condition overall to be satisfied. Defaults to false.
                        type: boolean
                      regions:
                        description: |-
                          The request must originate from one of the provided
                          countries/regions.
                          Format: A valid ISO 3166-1 alpha-2 code.
                        items:
                          type: string
                        type: array
                      requiredAccessLevels:
                        items:
                          description: |-
                            A list of other access levels defined in the same policy.
                            Referencing an AccessContextManagerAccessLevel which does not exist
                            is an error. All access levels listed must be granted for the
                            condition to be true.
                          oneOf:
                          - not:
                              required:
                              - external
                            required:
                            - name
                          - not:
                              anyOf:
                              - required:
                                - name
                              - required:
                                - namespace
                            required:
                            - external
                          properties:
                            external:
                              description: 'Allowed value: The `name` field of an
                                `AccessContextManagerAccessLevel` resource.'
                              type: string
                            name:
                              description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
                              type: string
                            namespace:
                              description: 'Namespace of the referent. More info:
                                https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
                              type: string
                          type: object
                        type: array
                    type: object
                  type: array
              required:
              - conditions
              type: object
            custom:
              description: "Custom access level conditions are set using the Cloud\
                \ Common Expression Language to represent the necessary conditions\
                \ for the level to apply to a request. \nSee CEL spec at: https://github.com/google/cel-spec."
              properties:
                expr:
                  description: "Represents a textual expression in the Common Expression\
                    \ Language (CEL) syntax. CEL is a C-like expression language.\n\
                    This page details the objects and attributes that are used to\
                    \ the build the CEL expressions for \ncustom access levels - https://cloud.google.com/access-context-manager/docs/custom-access-level-spec."
                  properties:
                    description:
                      description: Description of the expression.
                      type: string
                    expression:
                      description: Textual representation of an expression in Common
                        Expression Language syntax.
                      type: string
                    location:
                      description: String indicating the location of the expression
                        for error reporting, e.g. a file name and a position in the
                        file.
                      type: string
                    title:
                      description: Title for the expression, i.e. a short string describing
                        its purpose.
                      type: string
                  required:
                  - expression
                  type: object
              required:
              - expr
              type: object
            description:
              description: Description of the AccessLevel and its use. Does not affect
                behavior.
              type: string
            resourceID:
              description: Immutable. Optional. The name of the resource. Used for
                creation and acquisition. When unset, the value of `metadata.name`
                is used as the default.
              type: string
            title:
              description: Human readable title. Must be unique within the Policy.
              type: string
          required:
          - accessPolicyRef
          - title
          type: object
        status:
          properties:
            conditions:
              description: Conditions represent the latest available observation of
                the resource's current state.
              items:
                properties:
                  lastTransitionTime:
                    description: Last time the condition transitioned from one status
                      to another.
                    type: string
                  message:
                    description: Human-readable message indicating details about last
                      transition.
                    type: string
                  reason:
                    description: Unique, one-word, CamelCase reason for the condition's
                      last transition.
                    type: string
                  status:
                    description: Status is the status of the condition. Can be True,
                      False, Unknown.
                    type: string
                  type:
                    description: Type is the type of the condition.
                    type: string
                type: object
              type: array
            observedGeneration:
              description: ObservedGeneration is the generation of the resource that
                was most recently observed by the Config Connector controller. If
                this is equal to metadata.generation, then that means that the current
                reported status reflects the most recent desired state of the resource.
              type: integer
          type: object
      required:
      - spec
      type: object
        CodegenConfigurator config = new CodegenConfigurator();

        config.setInputSpec(specFile.toAbsolutePath().toString());
        config.setGeneratorName(MyOpenApiCodegen.class.getCanonicalName());
        config.setOutputDir(outputDir.toAbsolutePath().toString());

        final ClientOptInput clientOptInput = config.toClientOptInput();
        Generator generator = new DefaultGenerator();
        generator.opts(clientOptInput);
        generator.generate();

The customised codegen:

import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.vavr.collection.List;
import org.openapitools.codegen.CodegenModel;
import org.openapitools.codegen.languages.SpringCodegen;
import io.swagger.v3.oas.models.media.Schema;

public class MyOpenApiCodegen extends SpringCodegen {

    @Override
    public org.openapitools.codegen.CodegenModel fromModel(String name, Schema schema) {
        var ret = super.fromModel(name, schema);

        ret.imports.add(BaseObject.class.getSimpleName());

        boolean hasMetadata = List.ofAll(ret.requiredVars)
                .appendAll(ret.optionalVars)
                .find(prop -> prop.name.equals("metadata") && prop.dataType.equals(
                        V1ObjectMeta.class.getCanonicalName()))
                .isDefined();

        if (hasMetadata) {
            generateMetaModel(ret);
        }
        return ret;
    }

    private void generateMetaModel(CodegenModel model) {
        var metaModel = new CodegenModel();
        metaModel.name = BaseObject.class.getSimpleName();
        metaModel.classname = BaseObject.class.getCanonicalName();
        model.parentModel = metaModel;
        model.parent = BaseObject.class.getSimpleName();
    }

    @Override
    public String toBooleanGetter(String name) {
        return toGetter(name);
    }

}
Generation Details

As described above:
-g MyOpenApiCodegen.class.getCanonicalName()
-i inputFile (a small snippet is shown above)
-o a temporary output directory

Steps to reproduce

Copy paste the personalized Codegen (or just use "java" as an alternative).
Copy paste the given extract (specs) in a YAML file and use it as input.
Use the CLI within the same folder of the personalized Codegen or copy-paste the above java code.

Related issues/PRs

There are many instances of broken code, i.e.:
#12883

Suggest a fix

Investigate what is causing the broken generation.

@alfredomusumeci alfredomusumeci changed the title [BUG] Description [BUG] Broken Java code generated from yaml specification Jul 15, 2022
@Stromner
Copy link

Any update regarding this issue, maintainers? It's still generating broken Java code and incorrect imports in 6.2.0

@martin-mfg
Copy link
Contributor

Hi, could you please provide the complete input file you're using? OpenApiGenerator 6.0.1 doesn't accept the above snippet (java.lang.RuntimeException: Issues with the OpenAPI input. Possible causes: invalid/missing spec, malformed JSON/YAML files, etc.). Also, as far as I know, support for oneOf is still quite new and might have some problems.

@wing328
Copy link
Member

wing328 commented Nov 20, 2023

closing this as there's no update. please open a new issue with full spec instead. thanks.

@wing328 wing328 closed this as completed Nov 20, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants