io.micronaut.openapi.visitor.ConfigUtils Maven / Gradle / Ivy
/*
* Copyright 2017-2023 original authors
*
* 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
*
* https://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.micronaut.openapi.visitor;
import io.micronaut.context.ApplicationContextConfiguration;
import io.micronaut.context.env.Environment;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.convert.DefaultMutableConversionService;
import io.micronaut.core.convert.MutableConversionService;
import io.micronaut.core.io.scan.ClassPathResourceLoader;
import io.micronaut.core.io.scan.DefaultClassPathResourceLoader;
import io.micronaut.core.naming.conventions.StringConvention;
import io.micronaut.core.type.Argument;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.StringUtils;
import io.micronaut.inject.ast.ClassElement;
import io.micronaut.inject.visitor.VisitorContext;
import io.micronaut.openapi.visitor.group.GroupProperties;
import io.micronaut.openapi.visitor.group.OpenApiInfo;
import io.micronaut.openapi.visitor.group.RouterVersioningProperties;
import io.micronaut.openapi.visitor.security.InterceptUrlMapConverter;
import io.micronaut.openapi.visitor.security.InterceptUrlMapPattern;
import io.micronaut.openapi.visitor.security.SecurityProperties;
import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import static io.micronaut.core.util.StringUtils.EMPTY_STRING;
import static io.micronaut.openapi.visitor.ContextProperty.MICRONAUT_INTERNAL_CUSTOM_SCHEMAS;
import static io.micronaut.openapi.visitor.ContextProperty.MICRONAUT_INTERNAL_ENVIRONMENT;
import static io.micronaut.openapi.visitor.ContextProperty.MICRONAUT_INTERNAL_ENVIRONMENT_CREATED;
import static io.micronaut.openapi.visitor.ContextProperty.MICRONAUT_INTERNAL_EXPANDABLE_PROPERTIES;
import static io.micronaut.openapi.visitor.ContextProperty.MICRONAUT_INTERNAL_EXPANDABLE_PROPERTIES_LOADED;
import static io.micronaut.openapi.visitor.ContextProperty.MICRONAUT_INTERNAL_GROUPS;
import static io.micronaut.openapi.visitor.ContextProperty.MICRONAUT_INTERNAL_JACKSON_JSON_VIEW_ENABLED;
import static io.micronaut.openapi.visitor.ContextProperty.MICRONAUT_INTERNAL_OPENAPI_ENDPOINTS;
import static io.micronaut.openapi.visitor.ContextProperty.MICRONAUT_INTERNAL_OPENAPI_PROJECT_DIR;
import static io.micronaut.openapi.visitor.ContextProperty.MICRONAUT_INTERNAL_OPENAPI_PROPERTIES;
import static io.micronaut.openapi.visitor.ContextProperty.MICRONAUT_INTERNAL_ROUTER_VERSIONING_PROPERTIES;
import static io.micronaut.openapi.visitor.ContextProperty.MICRONAUT_INTERNAL_SCHEMA_DECORATORS;
import static io.micronaut.openapi.visitor.ContextProperty.MICRONAUT_INTERNAL_SECURITY_PROPERTIES;
import static io.micronaut.openapi.visitor.ContextUtils.ARGUMENT_CUSTOM_SCHEMA_MAP;
import static io.micronaut.openapi.visitor.ContextUtils.ARGUMENT_GROUP_PROPERTIES_MAP;
import static io.micronaut.openapi.visitor.ContextUtils.ARGUMENT_SCHEMA_DECORATORS_MAP;
import static io.micronaut.openapi.visitor.ContextUtils.EXPANDABLE_PROPERTIES_ARGUMENT;
import static io.micronaut.openapi.visitor.ContextUtils.warn;
import static io.micronaut.openapi.visitor.FileUtils.calcFinalFilename;
import static io.micronaut.openapi.visitor.FileUtils.resolve;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.ALL;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_ENVIRONMENT_ENABLED;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_JACKSON_JSON_VIEW_ENABLED;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_31_JSON_SCHEMA_DIALECT;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_ADOC_ENABLED;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_ADOC_OPENAPI_PATH;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_ADOC_OUTPUT_DIR_PATH;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_ADOC_OUTPUT_FILENAME;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_ADOC_TEMPLATES_DIR_PATH;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_ADOC_TEMPLATE_FILENAME;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_ADOC_TEMPLATE_PREFIX;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_CONFIG_FILE;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_ENABLED;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_ENVIRONMENTS;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_EXPAND_PREFIX;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_GENERATOR_EXTENSIONS_ENABLED;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_GROUPS;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_JSON_VIEW_DEFAULT_INCLUSION;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_PROJECT_DIR;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_SCHEMA;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_SCHEMA_DECORATOR_POSTFIX;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_SCHEMA_DECORATOR_PREFIX;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_SCHEMA_DUPLICATE_RESOLUTION;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_SCHEMA_EXTRA_ENABLED;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_SCHEMA_MAPPING;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_SCHEMA_NAME_SEPARATOR_EMPTY;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_SCHEMA_NAME_SEPARATOR_GENERIC;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_SCHEMA_NAME_SEPARATOR_INNER_CLASS;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_SCHEMA_POSTFIX;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_SCHEMA_PREFIX;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_SECURITY_DEFAULT_SCHEMA_NAME;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_SECURITY_ENABLED;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_SWAGGER_FILE_GENERATION_ENABLED;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_OPENAPI_VERSIONING_ENABLED;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.MICRONAUT_SERVER_CONTEXT_PATH;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.OPENAPI_CONFIG_FILE;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.SPRING_SERVER_CONTEXT_PATH;
import static io.micronaut.openapi.visitor.OpenApiConfigProperty.SPRING_WEBFLUX_BASE_PATH;
import static io.micronaut.openapi.visitor.StringUtil.COMMA;
import static io.micronaut.openapi.visitor.StringUtil.DOT;
import static io.micronaut.openapi.visitor.StringUtil.UNDERSCORE;
import static io.micronaut.openapi.visitor.StringUtil.WILDCARD;
import static io.micronaut.openapi.visitor.group.RouterVersioningProperties.DEFAULT_HEADER_NAME;
import static io.micronaut.openapi.visitor.group.RouterVersioningProperties.DEFAULT_PARAMETER_NAME;
/**
* Configuration utilities methods.
*
* @since 4.10.0
*/
@Internal
public final class ConfigUtils {
private static final String LOADED_POSTFIX = ".loaded";
private static final String VALUE_POSTFIX = ".value";
/**
* Default autogenerated security schema name.
*/
private static final String DEFAULT_SECURITY_SCHEMA_NAME = "Authorization";
private ConfigUtils() {
}
public static SchemaDecorator getSchemaDecoration(String packageName, VisitorContext context) {
Map schemaDecorators = ContextUtils.get(MICRONAUT_INTERNAL_SCHEMA_DECORATORS, ARGUMENT_SCHEMA_DECORATORS_MAP, context);
if (schemaDecorators != null) {
return schemaDecorators.get(packageName);
}
schemaDecorators = new HashMap<>();
// first read system properties
Properties sysProps = System.getProperties();
readSchemaDecorators(sysProps, schemaDecorators, context);
// second read openapi.properties file
Properties fileProps = readOpenApiConfigFile(context);
readSchemaDecorators(fileProps, schemaDecorators, context);
// third read environments properties
Environment environment = getEnv(context);
if (environment != null) {
for (Map.Entry entry : environment.getProperties(MICRONAUT_OPENAPI_SCHEMA_PREFIX, StringConvention.RAW).entrySet()) {
SchemaDecorator decorator = schemaDecorators.computeIfAbsent(entry.getKey(), k -> new SchemaDecorator());
decorator.setPrefix((String) entry.getValue());
}
for (Map.Entry entry : environment.getProperties(MICRONAUT_OPENAPI_SCHEMA_POSTFIX, StringConvention.RAW).entrySet()) {
SchemaDecorator decorator = schemaDecorators.computeIfAbsent(entry.getKey(), k -> new SchemaDecorator());
decorator.setPostfix((String) entry.getValue());
}
for (Map.Entry entry : environment.getProperties(MICRONAUT_OPENAPI_SCHEMA_DECORATOR_PREFIX, StringConvention.RAW).entrySet()) {
SchemaDecorator decorator = schemaDecorators.computeIfAbsent(entry.getKey(), k -> new SchemaDecorator());
decorator.setPrefix((String) entry.getValue());
}
for (Map.Entry entry : environment.getProperties(MICRONAUT_OPENAPI_SCHEMA_DECORATOR_POSTFIX, StringConvention.RAW).entrySet()) {
SchemaDecorator decorator = schemaDecorators.computeIfAbsent(entry.getKey(), k -> new SchemaDecorator());
decorator.setPostfix((String) entry.getValue());
}
}
ContextUtils.put(MICRONAUT_INTERNAL_SCHEMA_DECORATORS, schemaDecorators, context);
return schemaDecorators.get(packageName);
}
public static ClassElement getCustomSchema(String className, Map typeArgs, VisitorContext context) {
Map customSchemas = ContextUtils.get(MICRONAUT_INTERNAL_CUSTOM_SCHEMAS, ARGUMENT_CUSTOM_SCHEMA_MAP, context);
if (customSchemas != null) {
String key = getClassNameWithGenerics(className, typeArgs);
CustomSchema customSchema = customSchemas.get(key);
if (customSchema != null) {
return customSchema.classElement;
}
customSchema = customSchemas.get(className);
return customSchema != null ? customSchema.classElement : null;
}
customSchemas = new HashMap<>();
// first read system properties
Properties sysProps = System.getProperties();
readCustomSchemas(sysProps, customSchemas, context);
// second read openapi.properties file
Properties fileProps = readOpenApiConfigFile(context);
readCustomSchemas(fileProps, customSchemas, context);
// third read environments properties
Environment environment = getEnv(context);
if (environment != null) {
for (Map.Entry entry : environment.getProperties(MICRONAUT_OPENAPI_SCHEMA, StringConvention.RAW).entrySet()) {
String configuredClassName = entry.getKey();
// Remove this check, after we remove MICRONAUT_OPENAPI_SCHEMA property
String prop = MICRONAUT_OPENAPI_SCHEMA + StringUtil.DOT + configuredClassName;
if (isMicronautProperty(prop)) {
continue;
}
String targetClassName = (String) entry.getValue();
readCustomSchema(configuredClassName, targetClassName, customSchemas, context);
}
for (Map.Entry entry : environment.getProperties(MICRONAUT_OPENAPI_SCHEMA_MAPPING, StringConvention.RAW).entrySet()) {
String configuredClassName = entry.getKey();
// Remove this check, after we remove MICRONAUT_OPENAPI_SCHEMA property
String prop = MICRONAUT_OPENAPI_SCHEMA + StringUtil.DOT + configuredClassName;
if (isMicronautProperty(prop)) {
continue;
}
String targetClassName = (String) entry.getValue();
readCustomSchema(configuredClassName, targetClassName, customSchemas, context);
}
}
ContextUtils.put(MICRONAUT_INTERNAL_CUSTOM_SCHEMAS, customSchemas, context);
if (customSchemas.isEmpty()) {
return null;
}
String key = getClassNameWithGenerics(className, typeArgs);
CustomSchema customSchema = customSchemas.get(key);
if (customSchema != null) {
return customSchema.classElement;
}
customSchema = customSchemas.get(className);
return customSchema != null ? customSchema.classElement : null;
}
private static boolean isMicronautProperty(String prop) {
return prop.startsWith(MICRONAUT_OPENAPI_SCHEMA_PREFIX)
|| prop.startsWith(MICRONAUT_OPENAPI_SCHEMA_POSTFIX)
|| prop.startsWith(MICRONAUT_OPENAPI_SCHEMA_DECORATOR_PREFIX)
|| prop.startsWith(MICRONAUT_OPENAPI_SCHEMA_DECORATOR_POSTFIX)
|| prop.startsWith(MICRONAUT_OPENAPI_SCHEMA_NAME_SEPARATOR_EMPTY)
|| prop.startsWith(MICRONAUT_OPENAPI_SCHEMA_NAME_SEPARATOR_GENERIC)
|| prop.startsWith(MICRONAUT_OPENAPI_SCHEMA_NAME_SEPARATOR_INNER_CLASS)
|| prop.startsWith(MICRONAUT_OPENAPI_SCHEMA_DUPLICATE_RESOLUTION)
|| prop.startsWith(MICRONAUT_OPENAPI_SCHEMA_EXTRA_ENABLED);
}
private static String getClassNameWithGenerics(String className, Map typeArgs) {
var key = new StringBuilder(className);
if (!typeArgs.isEmpty()) {
key.append('<');
boolean isFirst = true;
for (ClassElement typeArg : typeArgs.values()) {
if (!isFirst) {
key.append(',');
}
key.append(typeArg.getName());
isFirst = false;
}
key.append('>');
}
return key.toString();
}
@NonNull
public static String getServerContextPath(VisitorContext context) {
var contextPath = getConfigProperty(MICRONAUT_SERVER_CONTEXT_PATH, context);
if (contextPath == null) {
contextPath = getConfigProperty(SPRING_SERVER_CONTEXT_PATH, context);
}
if (contextPath == null) {
contextPath = getConfigProperty(SPRING_WEBFLUX_BASE_PATH, context);
}
if (contextPath == null) {
contextPath = StringUtils.EMPTY_STRING;
}
return contextPath;
}
public static DuplicateResolution getSchemaDuplicateResolution(VisitorContext context) {
var value = getConfigProperty(MICRONAUT_OPENAPI_SCHEMA_DUPLICATE_RESOLUTION, context);
if (StringUtils.isNotEmpty(value) && DuplicateResolution.ERROR.name().equalsIgnoreCase(value)) {
return DuplicateResolution.ERROR;
}
return DuplicateResolution.AUTO;
}
public static boolean isOpenApiEnabled(VisitorContext context) {
boolean value = getBooleanProperty(MICRONAUT_OPENAPI_ENABLED, true, context);
System.setProperty(MICRONAUT_OPENAPI_ENABLED, Boolean.toString(value));
return value;
}
public static boolean isSchemaNameSeparatorEmpty(VisitorContext context) {
boolean value = getBooleanProperty(MICRONAUT_OPENAPI_SCHEMA_NAME_SEPARATOR_EMPTY, false, context);
System.setProperty(MICRONAUT_OPENAPI_SCHEMA_NAME_SEPARATOR_EMPTY, Boolean.toString(value));
return value;
}
public static String getGenericSeparator(VisitorContext context) {
if (isSchemaNameSeparatorEmpty(context)) {
return EMPTY_STRING;
}
var value = getConfigProperty(MICRONAUT_OPENAPI_SCHEMA_NAME_SEPARATOR_GENERIC, context);
return StringUtils.isNotEmpty(value) ? value : UNDERSCORE;
}
public static String getInnerClassSeparator(VisitorContext context) {
if (isSchemaNameSeparatorEmpty(context)) {
return EMPTY_STRING;
}
var value = getConfigProperty(MICRONAUT_OPENAPI_SCHEMA_NAME_SEPARATOR_INNER_CLASS, context);
return StringUtils.isNotEmpty(value) ? value : DOT;
}
public static String getJsonSchemaDialect(VisitorContext context) {
var value = getConfigProperty(MICRONAUT_OPENAPI_31_JSON_SCHEMA_DIALECT, context);
return StringUtils.isNotEmpty(value) ? value : null;
}
public static boolean isSpecGenerationEnabled(VisitorContext context) {
boolean value = getBooleanProperty(MICRONAUT_OPENAPI_SWAGGER_FILE_GENERATION_ENABLED, true, context);
System.setProperty(MICRONAUT_OPENAPI_SWAGGER_FILE_GENERATION_ENABLED, Boolean.toString(value));
return value;
}
public static boolean isExtraSchemasEnabled(VisitorContext context) {
return getBooleanProperty(MICRONAUT_OPENAPI_SCHEMA_EXTRA_ENABLED, true, context);
}
public static boolean isJsonViewDefaultInclusion(VisitorContext context) {
return getBooleanProperty(MICRONAUT_OPENAPI_JSON_VIEW_DEFAULT_INCLUSION, true, context);
}
public static boolean isGeneratorExtensionsEnabled(VisitorContext context) {
return getBooleanProperty(MICRONAUT_OPENAPI_GENERATOR_EXTENSIONS_ENABLED, true, context);
}
public static boolean isAdocEnabled(VisitorContext context) {
return getBooleanProperty(MICRONAUT_OPENAPI_ADOC_ENABLED, true, context);
}
public static List> getExpandableProperties(VisitorContext context) {
Boolean propertiesLoaded = ContextUtils.get(MICRONAUT_INTERNAL_EXPANDABLE_PROPERTIES_LOADED, Boolean.class, context);
if (propertiesLoaded != null) {
return ContextUtils.get(MICRONAUT_INTERNAL_EXPANDABLE_PROPERTIES, EXPANDABLE_PROPERTIES_ARGUMENT, context);
}
var expandableProperties = new ArrayList>();
var expandPrefix = MICRONAUT_OPENAPI_EXPAND_PREFIX + DOT;
// first, check system properties and environments config files
var env = (AnnProcessorEnvironment) getEnv(context);
Map propertiesFromEnv = null;
if (env != null) {
try {
propertiesFromEnv = env.getProperties(expandPrefix.substring(0, expandPrefix.length() - 1), null);
} catch (Exception e) {
warn("Error:\n" + Utils.printStackTrace(e), context);
}
}
var expandedPropsMap = new HashMap();
if (CollectionUtils.isNotEmpty(propertiesFromEnv)) {
for (Map.Entry entry : propertiesFromEnv.entrySet()) {
expandedPropsMap.put(entry.getKey(), entry.getValue().toString());
}
}
// next, read openapi.properties file
Properties openapiProps = readOpenApiConfigFile(context);
for (Map.Entry
© 2015 - 2025 Weber Informatics LLC | Privacy Policy