All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.fabric8.kubernetes.schema.generator.GeneratorSettings Maven / Gradle / Ivy

/*
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
// Ported from https://github.com/manusa/yakc/blob/9272d649bfe05cd536d417fec64dcf679877bd14/buildSrc/src/main/java/com/marcnuri/yakc/schema/GeneratorSettings.java
package io.fabric8.kubernetes.schema.generator;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Singular;

import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import static io.fabric8.kubernetes.schema.generator.schema.SchemaUtils.APPLICATION_JSON;
import static io.fabric8.kubernetes.schema.generator.schema.SchemaUtils.mergeSchemas;

@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class GeneratorSettings {
  private Logger logger;
  @Singular
  private List schemas;
  @Singular
  private List urls;
  private OpenAPI openAPI;
  private Map apiVersions;
  private File outputDirectory;
  private File generatedSourcesDirectory;
  private File overridesDirectory;
  /**
   * Base package for generated classes.
   * 

* Adjust with {@link #packageMappings} to place generated classes in different packages. */ private String packageName; /** * Target package for Sundrio-generated builder classes. */ private String builderPackage; /** * Whether to add buildable references to generated classes. * *

* This is required for Sundrio when the references buildables are located at different modules and packages. */ private boolean addBuildableReferences; @Singular private Map packageMappings; private final AtomicBoolean packageMappingsProcessed = new AtomicBoolean(false); /** * Allows configuring the mapping of OpenAPI references to Java class names. *

* We can use this to customize the name of a generated class. */ @Builder.Default private Properties refToClassNameMappings = new Properties(); /** * Allows configuring the mapping of OpenAPI references to existing Java classes. *

* This is useful when the OpenAPI schema references classes that are already present in the classpath and * we don't want to generate. */ @Builder.Default private Properties refToJavaTypeMappings = new Properties(); /** * Allows configuring the mapping of specific field names to other names. *

* This is currently needed as a workaround for Sundrio's issues when generating builders when a class has two * List or array fields with a plural and singular. *

* For example, when a class has both `List items` and `List item`, Sundrio generates a * fluent builder with conflicting names such as buildItem(int), buildFirstItem(), and so on. */ @Singular private Map fieldNameMappings; @Singular private Set skipGenerationRegexes; @Singular private Set includeGenerationRegexes; private boolean generateJavadoc; @Builder.Default private final String genericKubernetesResourceClass = "io.fabric8.kubernetes.api.model.GenericKubernetesResource"; @Builder.Default private final String hasMetadataClass = "io.fabric8.kubernetes.api.model.HasMetadata"; @Builder.Default private final String kubernetesResourceClass = "io.fabric8.kubernetes.api.model.KubernetesResource"; @Builder.Default private final String kubernetesResourceListClass = "io.fabric8.kubernetes.api.model.KubernetesResourceList"; @Builder.Default private final String objectMetaClass = "io.fabric8.kubernetes.api.model.ObjectMeta"; @Builder.Default private final String namespacedClass = "io.fabric8.kubernetes.api.model.Namespaced"; @Builder.Default private final String rawExtensionClass = "io.fabric8.kubernetes.api.model.runtime.RawExtension"; @Builder.Default private final List buildableReferences = Arrays.asList( "io.fabric8.kubernetes.api.model.LabelSelector", "io.fabric8.kubernetes.api.model.Container", "io.fabric8.kubernetes.api.model.PodTemplateSpec", "io.fabric8.kubernetes.api.model.ResourceRequirements", "io.fabric8.kubernetes.api.model.IntOrString", "io.fabric8.kubernetes.api.model.ObjectReference", "io.fabric8.kubernetes.api.model.LocalObjectReference", "io.fabric8.kubernetes.api.model.PersistentVolumeClaim", "io.fabric8.kubernetes.api.model.EnvVar", "io.fabric8.kubernetes.api.model.ContainerPort", "io.fabric8.kubernetes.api.model.Volume", "io.fabric8.kubernetes.api.model.VolumeMount"); public OpenAPI getOpenAPI() { synchronized (this) { if (openAPI == null) { final List openApis = new ArrayList<>(); schemas.stream().map(GeneratorUtils::parse).forEach(openApis::add); urls.stream().map(URI::create).map(GeneratorUtils::parse).forEach(openApis::add); openAPI = mergeSchemas(openApis); } } return openAPI; } public Map getApiVersions() { synchronized (this) { if (apiVersions == null) { apiVersions = computeApiVersions(getOpenAPI()); } } return apiVersions; } public synchronized File getGeneratedSourcesDirectory() { if (generatedSourcesDirectory == null) { generatedSourcesDirectory = getOutputDirectory().toPath() .resolve("src").resolve("generated").resolve("java").toFile(); } return generatedSourcesDirectory; } public synchronized File getOverridesDirectory() { if (overridesDirectory == null) { overridesDirectory = getOutputDirectory().toPath() .resolve("src").resolve("main").resolve("java").toFile(); } return overridesDirectory; } // Package Mappings // Need to be sorted in reverse order so that resolution favors grandParent.parent.child over grandParent.parent public Map getPackageMappings() { if (packageMappings == null) { return Collections.emptyMap(); } if (!packageMappingsProcessed.getAndSet(true)) { synchronized (this) { packageMappings = Collections.unmodifiableMap(packageMappings.keySet().stream() .sorted(Comparator.reverseOrder()) .collect(Collectors.toMap(k -> k, packageMappings::get, (v1, v2) -> v2, LinkedHashMap::new))); } } return packageMappings; } public String getHasMetadataClassSimpleName() { return hasMetadataClass.substring(hasMetadataClass.lastIndexOf('.') + 1); } public String getKubernetesResourceClassSimpleName() { return kubernetesResourceClass.substring(kubernetesResourceClass.lastIndexOf('.') + 1); } public String getKubernetesResourceListClassSimpleName() { return kubernetesResourceListClass.substring(kubernetesResourceListClass.lastIndexOf('.') + 1); } public String getNamespacedClassSimpleName() { return namespacedClass.substring(namespacedClass.lastIndexOf('.') + 1); } private static Map computeApiVersions(OpenAPI openAPI) { final Map apiVersions = new HashMap<>(); final List> apiPaths = openAPI.getPaths().entrySet().stream() .filter(e -> isApplicablePath(e.getValue())) .collect(Collectors.toList()); for (Map.Entry apiPath : apiPaths) { for (Operation operation : new Operation[] { apiPath.getValue().getGet(), apiPath.getValue().getPost() }) { if (operation == null || operation.getResponses().get("200") == null || operation.getResponses().get("200").getContent().get(APPLICATION_JSON) == null || operation.getExtensions() == null || operation.getExtensions().get("x-kubernetes-group-version-kind") == null) { continue; } final ApiVersion.ApiVersionBuilder apiVersionBuilder = ApiVersion.builder(); // final String path = apiPath.getKey(); // final String[] segments = path.split("/"); // if (path.startsWith("/apis/") && segments.length > 3) { // apiVersionBuilder.group(segments[2]).version(segments[3]); // } else { // apiVersionBuilder.group("").version("v1"); // } final Map groupVersionKind = (Map) operation.getExtensions() .get("x-kubernetes-group-version-kind"); apiVersionBuilder.group(groupVersionKind.get("group")); apiVersionBuilder.version(groupVersionKind.get("version")); apiVersionBuilder.namespaced(isNamespaced(operation)); final String ref = operation.getResponses().get("200").getContent().get(APPLICATION_JSON) .getSchema().get$ref(); if (ref != null && !ref.startsWith("#/components/schemas/io.k8s.apimachinery.pkg.apis")) { apiVersions.put(ref.replaceFirst("#/components/schemas/", ""), apiVersionBuilder.build()); } } } openAPI.getComponents().getSchemas().entrySet().stream() .filter(e -> e.getValue().getExtensions() != null) .filter(e -> e.getValue().getExtensions().containsKey("x-fabric8-info")) .forEach(e -> { final Map fabric8Info = (Map) e.getValue().getExtensions().get("x-fabric8-info"); // Consider only Kubernetes object and list types (top-level resources) if (!Objects.equals(fabric8Info.get("Type"), "nested")) { apiVersions.putIfAbsent(e.getKey(), ApiVersion.builder() .group(fabric8Info.get("Group").toString()) .version(fabric8Info.get("Version").toString()) .namespaced(Objects.equals(fabric8Info.get("Scope").toString(), "Namespaced")) .build()); } }); // api machinery + core components always tagged as core v1 // TODO: see if we want to omit the addition of group and version altogether in the future openAPI.getComponents().getSchemas().entrySet().stream() .filter(GeneratorSettings::isApplicableApiMachineryOrCoreComponent) .forEach(e -> apiVersions.putIfAbsent(e.getKey(), ApiVersion.builder().group("").version("v1").build())); return apiVersions; } private static boolean isApplicablePath(PathItem pathItem) { final Operation get = pathItem.getGet(); final Operation post = pathItem.getPost(); if (get == null && post == null) { return false; } // Lists (e.g. PodList) if (pathParameters(get).isEmpty() || (isNamespaced(get) && pathParameters(get).size() == 1)) { return true; } // Named resources (e.g. Pod, ComponentStatus) return pathParameters(get).stream().map(Parameter::getName).anyMatch("name"::equals) || pathParameters(post).stream().map(Parameter::getName).anyMatch("name"::equals); } private static boolean isApplicableApiMachineryOrCoreComponent(Map.Entry componentSchema) { if (!componentSchema.getKey().startsWith("io.k8s.apimachinery.pkg.apis") && !componentSchema.getKey().startsWith("io.k8s.api.core.v1.PodExecOptions")) { return false; } if (componentSchema.getValue().getProperties() == null) { return false; } final Set fields = componentSchema.getValue().getProperties().keySet(); return fields.contains("apiVersion") && fields.contains("kind"); } private static boolean isNamespaced(Operation operation) { final List parameters = pathParameters(operation); return parameters.stream().map(Parameter::getName).anyMatch("namespace"::equals); } private static List pathParameters(Operation operation) { if (operation == null || operation.getParameters() == null) { return Collections.emptyList(); } return operation.getParameters().stream().filter(p -> p.getIn().equals("path")) .collect(Collectors.toList()); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy