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

io.helidon.openapi.OpenApiFeatureConfig Maven / Gradle / Ivy

/*
 * Copyright (c) 2024 Oracle and/or its affiliates.
 *
 * 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.
 */

package io.helidon.openapi;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;

import io.helidon.builder.api.Prototype;
import io.helidon.common.Generated;
import io.helidon.common.HelidonServiceLoader;
import io.helidon.common.config.Config;
import io.helidon.cors.CrossOriginConfig;
import io.helidon.openapi.spi.OpenApiManagerProvider;
import io.helidon.openapi.spi.OpenApiServiceProvider;

/**
 * {@link OpenApiFeature} prototype.
 *
 * @see #builder()
 * @see #create()
 */
@Generated(value = "io.helidon.builder.codegen.BuilderCodegen", trigger = "io.helidon.openapi.OpenApiFeatureConfigBlueprint")
public interface OpenApiFeatureConfig extends OpenApiFeatureConfigBlueprint, Prototype.Api {

    /**
     * Create a new fluent API builder to customize configuration.
     *
     * @return a new builder
     */
    static OpenApiFeatureConfig.Builder builder() {
        return new OpenApiFeatureConfig.Builder();
    }

    /**
     * Create a new fluent API builder from an existing instance.
     *
     * @param instance an existing instance used as a base for the builder
     * @return a builder based on an instance
     */
    static OpenApiFeatureConfig.Builder builder(OpenApiFeatureConfig instance) {
        return OpenApiFeatureConfig.builder().from(instance);
    }

    /**
     * Create a new instance from configuration.
     *
     * @param config used to configure the new instance
     * @return a new instance configured from configuration
     */
    static OpenApiFeatureConfig create(Config config) {
        return OpenApiFeatureConfig.builder().config(config).buildPrototype();
    }

    /**
     * Create a new instance with default values.
     *
     * @return a new instance
     */
    static OpenApiFeatureConfig create() {
        return OpenApiFeatureConfig.builder().buildPrototype();
    }

    /**
     * Fluent API builder base for {@link OpenApiFeature}.
     *
     * @param  type of the builder extending this abstract builder
     * @param  type of the prototype interface that would be built by {@link #buildPrototype()}
     */
    abstract class BuilderBase, PROTOTYPE extends OpenApiFeatureConfig> implements Prototype.ConfiguredBuilder {

        private final List services = new ArrayList<>();
        private final List roles = new ArrayList<>(List.of("openapi"));
        private final Set sockets = new LinkedHashSet<>();
        private boolean isEnabled = true;
        private boolean isRolesMutated;
        private boolean isServicesMutated;
        private boolean managerDiscoverServices = false;
        private boolean permitAll = true;
        private boolean servicesDiscoverServices = true;
        private Config config;
        private CrossOriginConfig cors;
        private double weight = 90.0;
        private OpenApiManager manager;
        private String name = "openapi";
        private String staticFile;
        private String webContext = "/openapi";

        /**
         * Protected to support extensibility.
         */
        protected BuilderBase() {
        }

        /**
         * Update this builder from an existing prototype instance. This method disables automatic service discovery.
         *
         * @param prototype existing prototype to update this builder from
         * @return updated builder instance
         */
        public BUILDER from(OpenApiFeatureConfig prototype) {
            weight(prototype.weight());
            isEnabled(prototype.isEnabled());
            webContext(prototype.webContext());
            staticFile(prototype.staticFile());
            cors(prototype.cors());
            if (!isServicesMutated) {
                services.clear();
            }
            addServices(prototype.services());
            servicesDiscoverServices = false;
            manager(prototype.manager());
            managerDiscoverServices = false;
            permitAll(prototype.permitAll());
            if (!isRolesMutated) {
                roles.clear();
            }
            addRoles(prototype.roles());
            name(prototype.name());
            addSockets(prototype.sockets());
            return self();
        }

        /**
         * Update this builder from an existing prototype builder instance.
         *
         * @param builder existing builder prototype to update this builder from
         * @return updated builder instance
         */
        public BUILDER from(OpenApiFeatureConfig.BuilderBase builder) {
            weight(builder.weight());
            isEnabled(builder.isEnabled());
            webContext(builder.webContext());
            builder.staticFile().ifPresent(this::staticFile);
            builder.cors().ifPresent(this::cors);
            if (isServicesMutated) {
                if (builder.isServicesMutated) {
                    addServices(builder.services);
                }
            } else {
                services.clear();
                addServices(builder.services);
            }
            servicesDiscoverServices = builder.servicesDiscoverServices;
            builder.manager().ifPresent(this::manager);
            managerDiscoverServices = builder.managerDiscoverServices;
            permitAll(builder.permitAll());
            if (isRolesMutated) {
                if (builder.isRolesMutated) {
                    addRoles(builder.roles);
                }
            } else {
                roles.clear();
                addRoles(builder.roles);
            }
            name(builder.name());
            addSockets(builder.sockets);
            return self();
        }

        /**
         * Update builder from configuration (node of this type).
         * If a value is present in configuration, it would override currently configured values.
         *
         * @param config configuration instance used to obtain values to update this builder
         * @return updated builder instance
         */
        @Override
        public BUILDER config(Config config) {
            Objects.requireNonNull(config);
            this.config = config;
            config.get("weight").as(Double.class).ifPresent(this::weight);
            config.get("enabled").as(Boolean.class).ifPresent(this::isEnabled);
            config.get("web-context").as(String.class).ifPresent(this::webContext);
            config.get("static-file").as(String.class).ifPresent(this::staticFile);
            config.get("cors").map(CrossOriginConfig::create).ifPresent(this::cors);
            config.get("permit-all").as(Boolean.class).ifPresent(this::permitAll);
            config.get("roles").asList(String.class).ifPresent(this::roles);
            config.get("sockets").asList(String.class).map(java.util.Set::copyOf).ifPresent(this::sockets);
            return self();
        }

        /**
         * Weight of the OpenAPI feature. This is quite low, to be registered after routing.
         * {@value io.helidon.openapi.OpenApiFeature#WEIGHT}.
         *
         * @param weight weight of the feature
         * @return updated builder instance
         * @see #weight()
         */
        public BUILDER weight(double weight) {
            this.weight = weight;
            return self();
        }

        /**
         * Sets whether the feature should be enabled.
         *
         * @param isEnabled {@code true} if enabled, {@code false} otherwise
         * @return updated builder instance
         * @see #isEnabled()
         */
        public BUILDER isEnabled(boolean isEnabled) {
            this.isEnabled = isEnabled;
            return self();
        }

        /**
         * Web context path for the OpenAPI endpoint.
         *
         * @param webContext webContext to use
         * @return updated builder instance
         * @see #webContext()
         */
        public BUILDER webContext(String webContext) {
            Objects.requireNonNull(webContext);
            this.webContext = webContext;
            return self();
        }

        /**
         * Clear existing value of this property.
         *
         * @return updated builder instance
         * @see #staticFile()
         */
        public BUILDER clearStaticFile() {
            this.staticFile = null;
            return self();
        }

        /**
         * Path of the static OpenAPI document file. Default types are `json`, `yaml`, and `yml`.
         *
         * @param staticFile location of the static OpenAPI document file
         * @return updated builder instance
         * @see #staticFile()
         */
        public BUILDER staticFile(String staticFile) {
            Objects.requireNonNull(staticFile);
            this.staticFile = staticFile;
            return self();
        }

        /**
         * Clear existing value of this property.
         *
         * @return updated builder instance
         * @see #cors()
         */
        public BUILDER clearCors() {
            this.cors = null;
            return self();
        }

        /**
         * CORS config.
         *
         * @param cors CORS config
         * @return updated builder instance
         * @see #cors()
         */
        public BUILDER cors(CrossOriginConfig cors) {
            Objects.requireNonNull(cors);
            this.cors = cors;
            return self();
        }

        /**
         * CORS config.
         *
         * @param consumer CORS config
         * @return updated builder instance
         * @see #cors()
         */
        public BUILDER cors(Consumer consumer) {
            Objects.requireNonNull(consumer);
            var builder = CrossOriginConfig.builder();
            consumer.accept(builder);
            this.cors(builder.build());
            return self();
        }

        /**
         * OpenAPI services.
         *
         * @param discoverServices whether to discover implementations through service loader
         * @return updated builder instance
         * @see #services()
         */
        public BUILDER servicesDiscoverServices(boolean discoverServices) {
            this.servicesDiscoverServices = discoverServices;
            return self();
        }

        /**
         * OpenAPI services.
         *
         * @param services the OpenAPI services
         * @return updated builder instance
         * @see #services()
         */
        public BUILDER services(List services) {
            Objects.requireNonNull(services);
            isServicesMutated = true;
            this.services.clear();
            this.services.addAll(services);
            return self();
        }

        /**
         * OpenAPI services.
         *
         * @param services the OpenAPI services
         * @return updated builder instance
         * @see #services()
         */
        public BUILDER addServices(List services) {
            Objects.requireNonNull(services);
            isServicesMutated = true;
            this.services.addAll(services);
            return self();
        }

        /**
         * OpenAPI services.
         *
         * @param service the OpenAPI services
         * @return updated builder instance
         * @see #services()
         */
        public BUILDER addService(OpenApiService service) {
            Objects.requireNonNull(service);
            this.services.add(service);
            isServicesMutated = true;
            return self();
        }

        /**
         * Clear existing value of this property.
         *
         * @return updated builder instance
         * @see #manager()
         */
        public BUILDER clearManager() {
            this.manager = null;
            return self();
        }

        /**
         * OpenAPI manager.
         *
         * @param manager the OpenAPI manager
         * @return updated builder instance
         * @see #manager()
         */
        public BUILDER manager(OpenApiManager manager) {
            Objects.requireNonNull(manager);
            this.manager = manager;
            return self();
        }

        /**
         * Whether to allow anybody to access the endpoint.
         *
         * @param permitAll whether to permit access to metrics endpoint to anybody, defaults to {@code true}
         * @return updated builder instance
         * @see #roles()
         * @see #permitAll()
         */
        public BUILDER permitAll(boolean permitAll) {
            this.permitAll = permitAll;
            return self();
        }

        /**
         * Hints for role names the user is expected to be in.
         *
         * @param roles list of hints
         * @return updated builder instance
         * @see #roles()
         */
        public BUILDER roles(List roles) {
            Objects.requireNonNull(roles);
            isRolesMutated = true;
            this.roles.clear();
            this.roles.addAll(roles);
            return self();
        }

        /**
         * Hints for role names the user is expected to be in.
         *
         * @param roles list of hints
         * @return updated builder instance
         * @see #roles()
         */
        public BUILDER addRoles(List roles) {
            Objects.requireNonNull(roles);
            isRolesMutated = true;
            this.roles.addAll(roles);
            return self();
        }

        /**
         * Name of this instance.
         *
         * @param name instance name, used when discovered from configuration
         * @return updated builder instance
         * @see #name()
         */
        public BUILDER name(String name) {
            Objects.requireNonNull(name);
            this.name = name;
            return self();
        }

        /**
         * List of sockets to register this feature on. If empty, it would get registered on all sockets.
         *
         * @param sockets socket names to register on, defaults to empty (all available sockets)
         * @return updated builder instance
         * @see #sockets()
         */
        public BUILDER sockets(Set sockets) {
            Objects.requireNonNull(sockets);
            this.sockets.clear();
            this.sockets.addAll(sockets);
            return self();
        }

        /**
         * List of sockets to register this feature on. If empty, it would get registered on all sockets.
         *
         * @param sockets socket names to register on, defaults to empty (all available sockets)
         * @return updated builder instance
         * @see #sockets()
         */
        public BUILDER addSockets(Set sockets) {
            Objects.requireNonNull(sockets);
            this.sockets.addAll(sockets);
            return self();
        }

        /**
         * List of sockets to register this feature on. If empty, it would get registered on all sockets.
         *
         * @param socket socket names to register on, defaults to empty (all available sockets)
         * @return updated builder instance
         * @see #sockets()
         */
        public BUILDER addSocket(String socket) {
            Objects.requireNonNull(socket);
            this.sockets.add(socket);
            return self();
        }

        /**
         * Weight of the OpenAPI feature. This is quite low, to be registered after routing.
         * {@value io.helidon.openapi.OpenApiFeature#WEIGHT}.
         *
         * @return the weight
         */
        public double weight() {
            return weight;
        }

        /**
         * Sets whether the feature should be enabled.
         *
         * @return the is enabled
         */
        public boolean isEnabled() {
            return isEnabled;
        }

        /**
         * Web context path for the OpenAPI endpoint.
         *
         * @return the web context
         */
        public String webContext() {
            return webContext;
        }

        /**
         * Path of the static OpenAPI document file. Default types are `json`, `yaml`, and `yml`.
         *
         * @return the static file
         */
        public Optional staticFile() {
            return Optional.ofNullable(staticFile);
        }

        /**
         * CORS config.
         *
         * @return the cors
         */
        public Optional cors() {
            return Optional.ofNullable(cors);
        }

        /**
         * OpenAPI services.
         *
         * @return the services
         */
        public List services() {
            return services;
        }

        /**
         * OpenAPI manager.
         *
         * @return the manager
         */
        public Optional> manager() {
            return Optional.ofNullable(manager);
        }

        /**
         * Whether to allow anybody to access the endpoint.
         *
         * @return the permit all
         * @see #roles()
         * @see #permitAll()
         */
        public boolean permitAll() {
            return permitAll;
        }

        /**
         * Hints for role names the user is expected to be in.
         *
         * @return the roles
         */
        public List roles() {
            return roles;
        }

        /**
         * Name of this instance.
         *
         * @return the name
         */
        public String name() {
            return name;
        }

        /**
         * List of sockets to register this feature on. If empty, it would get registered on all sockets.
         *
         * @return the sockets
         */
        public Set sockets() {
            return sockets;
        }

        /**
         * If this instance was configured, this would be the config instance used.
         *
         * @return config node used to configure this builder, or empty if not configured
         */
        public Optional config() {
            return Optional.ofNullable(config);
        }

        @Override
        public String toString() {
            return "OpenApiFeatureConfigBuilder{"
                    + "weight=" + weight + ","
                    + "isEnabled=" + isEnabled + ","
                    + "webContext=" + webContext + ","
                    + "staticFile=" + staticFile + ","
                    + "cors=" + cors + ","
                    + "services=" + services + ","
                    + "manager=" + manager + ","
                    + "permitAll=" + permitAll + ","
                    + "roles=" + roles + ","
                    + "name=" + name + ","
                    + "sockets=" + sockets
                    + "}";
        }

        /**
         * Handles providers and decorators.
         */
        @SuppressWarnings("unchecked")
        protected void preBuildPrototype() {
            var config = this.config == null ? Config.empty() : this.config;
            {
                var serviceLoader = HelidonServiceLoader.create(ServiceLoader.load(OpenApiServiceProvider.class));
                this.addServices(discoverServices(config, "services", serviceLoader, OpenApiServiceProvider.class, OpenApiService.class, servicesDiscoverServices, services));
            }
            {
                var serviceLoader = HelidonServiceLoader.create(ServiceLoader.load(OpenApiManagerProvider.class));
                discoverService(config, "manager", serviceLoader, OpenApiManagerProvider.class, OpenApiManager.class, managerDiscoverServices, Optional.ofNullable(manager)).ifPresent(this::manager);
            }
        }

        /**
         * Validates required properties.
         */
        protected void validatePrototype() {
        }

        /**
         * Path of the static OpenAPI document file. Default types are `json`, `yaml`, and `yml`.
         *
         * @param staticFile location of the static OpenAPI document file
         * @return updated builder instance
         * @see #staticFile()
         */
        BUILDER staticFile(Optional staticFile) {
            Objects.requireNonNull(staticFile);
            this.staticFile = staticFile.map(java.lang.String.class::cast).orElse(this.staticFile);
            return self();
        }

        /**
         * CORS config.
         *
         * @param cors CORS config
         * @return updated builder instance
         * @see #cors()
         */
        BUILDER cors(Optional cors) {
            Objects.requireNonNull(cors);
            this.cors = cors.map(io.helidon.cors.CrossOriginConfig.class::cast).orElse(this.cors);
            return self();
        }

        /**
         * OpenAPI manager.
         *
         * @param manager the OpenAPI manager
         * @return updated builder instance
         * @see #manager()
         */
        BUILDER manager(Optional> manager) {
            Objects.requireNonNull(manager);
            this.manager = manager.map(io.helidon.openapi.OpenApiManager.class::cast).orElse(this.manager);
            return self();
        }

        /**
         * Generated implementation of the prototype, can be extended by descendant prototype implementations.
         */
        protected static class OpenApiFeatureConfigImpl implements OpenApiFeatureConfig, Supplier {

            private final boolean isEnabled;
            private final boolean permitAll;
            private final double weight;
            private final List services;
            private final List roles;
            private final Optional cors;
            private final Optional> manager;
            private final Optional staticFile;
            private final Set sockets;
            private final String name;
            private final String webContext;

            /**
             * Create an instance providing a builder.
             *
             * @param builder extending builder base of this prototype
             */
            protected OpenApiFeatureConfigImpl(OpenApiFeatureConfig.BuilderBase builder) {
                this.weight = builder.weight();
                this.isEnabled = builder.isEnabled();
                this.webContext = builder.webContext();
                this.staticFile = builder.staticFile();
                this.cors = builder.cors();
                this.services = List.copyOf(builder.services());
                this.manager = builder.manager();
                this.permitAll = builder.permitAll();
                this.roles = List.copyOf(builder.roles());
                this.name = builder.name();
                this.sockets = Collections.unmodifiableSet(new LinkedHashSet<>(builder.sockets()));
            }

            @Override
            public OpenApiFeature build() {
                return OpenApiFeature.create(this);
            }

            @Override
            public OpenApiFeature get() {
                return build();
            }

            @Override
            public double weight() {
                return weight;
            }

            @Override
            public boolean isEnabled() {
                return isEnabled;
            }

            @Override
            public String webContext() {
                return webContext;
            }

            @Override
            public Optional staticFile() {
                return staticFile;
            }

            @Override
            public Optional cors() {
                return cors;
            }

            @Override
            public List services() {
                return services;
            }

            @Override
            public Optional> manager() {
                return manager;
            }

            @Override
            public boolean permitAll() {
                return permitAll;
            }

            @Override
            public List roles() {
                return roles;
            }

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

            @Override
            public Set sockets() {
                return sockets;
            }

            @Override
            public String toString() {
                return "OpenApiFeatureConfig{"
                        + "weight=" + weight + ","
                        + "isEnabled=" + isEnabled + ","
                        + "webContext=" + webContext + ","
                        + "staticFile=" + staticFile + ","
                        + "cors=" + cors + ","
                        + "services=" + services + ","
                        + "manager=" + manager + ","
                        + "permitAll=" + permitAll + ","
                        + "roles=" + roles + ","
                        + "name=" + name + ","
                        + "sockets=" + sockets
                        + "}";
            }

            @Override
            public boolean equals(Object o) {
                if (o == this) {
                    return true;
                }
                if (!(o instanceof OpenApiFeatureConfig other)) {
                    return false;
                }
                return weight == other.weight()
                    && isEnabled == other.isEnabled()
                    && Objects.equals(webContext, other.webContext())
                    && Objects.equals(staticFile, other.staticFile())
                    && Objects.equals(cors, other.cors())
                    && Objects.equals(services, other.services())
                    && Objects.equals(manager, other.manager())
                    && permitAll == other.permitAll()
                    && Objects.equals(roles, other.roles())
                    && Objects.equals(name, other.name())
                    && Objects.equals(sockets, other.sockets());
            }

            @Override
            public int hashCode() {
                return Objects.hash(weight, isEnabled, webContext, staticFile, cors, services, manager, permitAll, roles, name, sockets);
            }

        }

    }

    /**
     * Fluent API builder for {@link OpenApiFeature}.
     */
    class Builder extends OpenApiFeatureConfig.BuilderBase implements io.helidon.common.Builder {

        private Builder() {
        }

        @Override
        public OpenApiFeatureConfig buildPrototype() {
            preBuildPrototype();
            validatePrototype();
            return new OpenApiFeatureConfigImpl(this);
        }

        @Override
        public OpenApiFeature build() {
            return OpenApiFeature.create(this.buildPrototype());
        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy