swagger-api / swagger-api/swagger-codegen
[JAVA] Different files generated when using the CLI and when using the generator alone
Nobody has claimed this yet.
- Dominant language
- Mustache
- Stars
- 17.8k
- Forks
- 6k
- PR merge metrics
- No merged PRs in 30d
Description
Description
Different java classes are generated when using the CLI or utilising its composing functions (182 items against 1000+ when using the CLI).
Swagger-codegen version
3.0.34
Swagger declaration file content or url
Extract of the used spec file:
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
Personalised codegen:
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.swagger.codegen.v3.CodegenModel;
import io.swagger.codegen.v3.generators.java.SpringCodegen;
import io.swagger.v3.oas.models.media.Schema;
import io.vavr.collection.List;
/**
* This class defines the specific implementation of the CodeGenerator.
*/
public class MyCodegen extends SpringCodegen {
/**
* Create a new instance of a model from an existing one and
* additional metadata.
* @param name The model's name.
* @param schema The model's schema.
* @param allSchemas The model's set of schemas.
* @return The generated model.
*/
@Override
public io.swagger.codegen.v3.CodegenModel fromModel(String name, Schema schema,
java.util.Map<String, Schema> allSchemas) {
var ret = super.fromModel(name, schema, allSchemas);
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;
}
/**
* Generate CodegenModel with the previously fetched metadata.
* @param model The model whose metadata is to be used.
*/
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();
}
/**
* Take a property name and return the getter name for it.
* This is due to a hateful combination of java primitives and java properties.
* All the boolean properties are generated as 'Boolean' not 'boolean',
* since they are all optional. Swagger generates a 'Boolean' getter as 'is<Foo>'.
* Java properties want all non-'boolean' property getters to be 'get<Foo>'
* The Yamlifier thus can't find a getter, which means it's not a property it can write.
* This is fixed in the OpenAPI generator but not the Swagger generator.
* @param name The property name.
* @return The getter name.
*/
@Override
public String toBooleanGetter(String name) {
return toGetter(name);
}
}
Code to perform generation if not using the CLI:
OpenAPI openAPI = new OpenAPIV3Parser().read(specFile.toAbsolutePath().toString(), null, null);
ClientOptInput clientOpts = new ClientOptInput().openAPI(openAPI);
CodegenConfig codegenConfig = CodegenConfigLoader.forName("spring");
codegenConfig.setOutputDir(outputDir.toAbsolutePath().toString());
clientOpts.setConfig(codegenConfig);
ClientOpts clientOps = new ClientOpts();
clientOps.setProperties(HashMap.of(
"hideGenerationTimestamp", "true",
"notNullJacksonAnnotation", "true"
).toJavaMap());
clientOpts.setOpts(clientOps);
Generator gen = new DefaultGenerator().opts(clientOpts);
gen.generate();
Code to perform generation using the CL (the output is just a temporary directory):
var args = List.of(
"generate",
"-i", specFile.toAbsolutePath().toString(),
"-l", MyCodegen.class.getCanonicalName(),
"-o", outputDir.toAbsolutePath().toString());
Command line used for generation
As above
Steps to reproduce
Run the code with the provided spec file (copy-paste into a YAML file), and try once without the CLI utilizing the above functions and then with the CLI. Notice the difference in the generated files.
The CLI should generate 14 files whereas the other solution will only generate 1.
Related issues/PRs
None
Suggest a fix/enhancement
Investigate what is the difference in the two procedures.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by comparing generation through the CLI with generation through the generator's composing functions, using the supplied OpenAPI YAML extract and version 3.0.34. Trace why the two entry points produce 182 versus more than 1,000 Java classes; done means both paths generate the same class set for the same specification.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- cli, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 30/100