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

io.swagger.v3.parser.processors.ExternalRefProcessor Maven / Gradle / Ivy

The newest version!
package io.swagger.v3.parser.processors;


import java.net.URI;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import io.swagger.v3.oas.models.Components;
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.callbacks.Callback;
import io.swagger.v3.oas.models.examples.Example;
import io.swagger.v3.oas.models.headers.Header;
import io.swagger.v3.oas.models.links.Link;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.ComposedSchema;
import io.swagger.v3.oas.models.media.Discriminator;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.parameters.RequestBody;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.parser.ResolverCache;
import io.swagger.v3.parser.models.RefFormat;
import io.swagger.v3.parser.models.RefType;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.LoggerFactory;

import static io.swagger.v3.parser.util.RefUtils.computeDefinitionName;
import static io.swagger.v3.parser.util.RefUtils.computeRefFormat;
import static io.swagger.v3.parser.util.RefUtils.getExternalPath;
import static io.swagger.v3.parser.util.RefUtils.isAnExternalRefFormat;

public final class ExternalRefProcessor {
    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ExternalRefProcessor.class);

    private final ResolverCache cache;
    private final OpenAPI openAPI;

    public ExternalRefProcessor(ResolverCache cache, OpenAPI openAPI) {
        this.cache = cache;
        this.openAPI = openAPI;
    }

    private String finalNameRec(Map schemas, String possiblyConflictingDefinitionName, Schema newSchema,
        int iteration) {
        String tryName =
            iteration == 0 ? possiblyConflictingDefinitionName : possiblyConflictingDefinitionName + "_" + iteration;
        Schema existingModel = schemas.get(tryName);
        if (existingModel != null) {
            if (existingModel.get$ref() != null) {
                // use the new model
                existingModel = null;
            } else if (!newSchema.equals(existingModel)) {
                if(cache.getResolutionCache().get(newSchema.get$ref())!= null){
                    return tryName;
                }
                LOGGER.debug("A model for " + existingModel + " already exists");
                return finalNameRec(schemas, possiblyConflictingDefinitionName, newSchema, ++iteration);
            }
        }else{
            // validate the name
            if(existingModel == null){
                for(String name: schemas.keySet()){
                    if(name.toLowerCase().equals(tryName.toLowerCase())){
                        existingModel = schemas.get(name);
                        tryName = name;
                        break;
                    }
                }
            }
        }
        return tryName;
    }

    public String processRefToExternalSchema(String $ref, RefFormat refFormat) {
        String renamedRef = cache.getRenamedRef($ref);
        if(renamedRef != null) {
            return renamedRef;
        }

        final Schema schema = cache.loadRef($ref, refFormat, Schema.class);

        if(schema == null) {
            // stop!  There's a problem.  retain the original ref
            LOGGER.warn("unable to load model reference from `" + $ref + "`.  It may not be available " +
                    "or the reference isn't a valid model schema");
            return $ref;
        }
        String newRef;

        if (openAPI.getComponents() == null) {
            openAPI.setComponents(new Components());
        }
        Map schemas = openAPI.getComponents().getSchemas();

        if (schemas == null) {
            schemas = new LinkedHashMap<>();
        }

        final String possiblyConflictingDefinitionName = computeDefinitionName($ref);
        newRef = finalNameRec(schemas, possiblyConflictingDefinitionName, schema, 0);
        cache.putRenamedRef($ref, newRef);
        Schema existingModel = schemas.get(newRef);
       if(existingModel != null && existingModel.get$ref() != null) {
            // use the new model
            existingModel = null;
        }

        if(existingModel == null) {
            // don't overwrite existing model reference
            openAPI.getComponents().addSchemas(newRef, schema);
            cache.addReferencedKey(newRef);

            String file = $ref.split("#/")[0];
            if (schema.get$ref() != null) {
                RefFormat ref = computeRefFormat(schema.get$ref());
                if (isAnExternalRefFormat(ref)) {
                    if (!ref.equals(RefFormat.URL)) {
                        String schemaFullRef = schema.get$ref();
                        String parent = (file.contains("/")) ? file.substring(0, file.lastIndexOf('/')) : "";
                        if (!parent.isEmpty() && !schemaFullRef.startsWith("/")) {
                            if (schemaFullRef.contains("#/")) {
                                String[] parts = schemaFullRef.split("#/");
                                String schemaFullRefFilePart = parts[0];
                                String schemaFullRefInternalRefPart = parts[1];
                                schemaFullRef = Paths.get(parent, schemaFullRefFilePart).normalize() + "#/" + schemaFullRefInternalRefPart;
                            } else {
                                schemaFullRef = Paths.get(parent, schemaFullRef).normalize().toString();
                            }
                            schemaFullRef = FilenameUtils.separatorsToUnix(schemaFullRef);
                        }
                        schema.set$ref(processRefToExternalSchema(schemaFullRef, ref));
                    }
                } else {
                    processRefToExternalSchema(file + schema.get$ref(), RefFormat.RELATIVE);
                }
            }


            if(schema instanceof ComposedSchema){
                ComposedSchema composedSchema = (ComposedSchema) schema;
                processComposedSchema(composedSchema, file);
            }
            //Loop the properties and recursively call this method;
            Map subProps = schema.getProperties();

            processProperties(subProps,file);

            processDiscriminator(schema.getDiscriminator(),file);

            if(schema.getAdditionalProperties() != null && schema.getAdditionalProperties() instanceof Schema){
                Schema additionalProperty = (Schema) schema.getAdditionalProperties();
                if (additionalProperty.get$ref() != null) {
                    processRefSchema(additionalProperty, file);
                } else if (additionalProperty instanceof ArraySchema) {
                    ArraySchema arrayProp = (ArraySchema) additionalProperty;
                    if (arrayProp.getItems() != null && arrayProp.getItems().get$ref() != null &&
                            StringUtils.isNotBlank(arrayProp.get$ref())) {
                        processRefSchema(arrayProp.getItems(), file);
                    }
                } else if (additionalProperty.getAdditionalProperties() != null && additionalProperty.getAdditionalProperties() instanceof Schema) {
                    Schema mapProp =  (Schema) additionalProperty.getAdditionalProperties();
                    if (mapProp.get$ref() != null) {
                        processRefSchema(mapProp, file);
                    } else if (mapProp.getAdditionalProperties() instanceof ArraySchema &&
                                ((ArraySchema) mapProp).getItems() != null &&
                                    ((ArraySchema) mapProp).getItems().get$ref() != null
                                    && StringUtils.isNotBlank(((ArraySchema) mapProp).getItems().get$ref()))  {
                        processRefSchema(((ArraySchema) mapProp).getItems(), file);
                    }
                }

            }
            if (schema instanceof ArraySchema && ((ArraySchema) schema).getItems() != null) {
                ArraySchema arraySchema = (ArraySchema) schema;
                if (StringUtils.isNotBlank(arraySchema.getItems().get$ref())) {
                    processRefSchema(((ArraySchema) schema).getItems(), file);
                } else {
                    if (arraySchema.getItems() instanceof ComposedSchema) {
                        ComposedSchema composedSchema = (ComposedSchema) arraySchema.getItems();
                        processComposedSchema(composedSchema, file);
                    }
                    processProperties(arraySchema.getItems().getProperties(), file);
                }
            }
        }
        return newRef;
    }

    private void processComposedSchema(ComposedSchema composedSchema, String file) {
        if (composedSchema.getAllOf() != null) {
            for (Schema item : composedSchema.getAllOf()) {
                if (item.get$ref() != null) {
                    processRefSchema(item, file);
                } else {
                    processSchema(item, file);
                }
            }

        }
        if (composedSchema.getOneOf() != null) {
            for (Schema item : composedSchema.getOneOf()) {
                if (item.get$ref() != null) {
                    if (item.get$ref() != null) {
                        processRefSchema(item, file);
                    } else {
                        processSchema(item, file);
                    }
                }
            }
        }
        if (composedSchema.getAnyOf() != null) {
            for (Schema item : composedSchema.getAnyOf()) {
                if (item.get$ref() != null) {
                    if (item.get$ref() != null) {
                        processRefSchema(item, file);
                    } else {
                        processSchema(item, file);
                    }
                }
            }
        }
    }

    private void processSchema(Schema property, String file) {
        if (property != null) {
            if (StringUtils.isNotBlank(property.get$ref())) {
                processRefSchema(property, file);
            }
            if (property.getProperties() != null) {
                processProperties(property.getProperties(), file);
            }
            if (property instanceof ArraySchema) {
                processSchema(((ArraySchema) property).getItems(), file);
            }
            if (property.getAdditionalProperties() instanceof Schema) {
                processSchema(((Schema) property.getAdditionalProperties()), file);
            }
            if (property instanceof ComposedSchema) {
                ComposedSchema composed = (ComposedSchema) property;
                final Map refMap = Optional.ofNullable(composed.getDiscriminator())
                                .map(Discriminator::getMapping).orElse(Collections.emptyMap()).entrySet()
                                .stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
                Map refCache = (!refMap.isEmpty() &&
                        (composed.getAnyOf() != null || composed.getOneOf() != null)) ? Stream.of(
                                composed.getAnyOf(), composed.getOneOf()
                        )
                            .filter(Objects::nonNull)
                            .filter(l -> !l.isEmpty())
                            .flatMap(Collection::stream)
                            .filter(s -> s.get$ref() != null)
                            .collect(Collectors.toMap(Schema::get$ref, Function.identity())) : Collections.emptyMap();

                processProperties(composed.getAllOf(), file);
                processProperties(composed.getAnyOf(), file);
                processProperties(composed.getOneOf(), file);

                if (!refMap.isEmpty() && !refCache.isEmpty()) {
                    refCache.entrySet()
                            .stream().filter(e -> !e.getKey().equals(e.getValue().get$ref()))
                            .forEach(entry -> {
                                String newRef = entry.getValue().get$ref();
                                property.getDiscriminator().getMapping().put(refMap.get(entry.getKey()), newRef);
                            });
                }
            }
        }
    }

    private void processProperties(Collection properties, String file) {
        if (properties != null) {
            for (Schema property : properties) {
                processSchema(property, file);
            }
        }
    }

    private void processProperties(Map properties, String file) {
        if (properties != null) {
            processProperties(properties.values(), file);
        }
    }


    public PathItem processRefToExternalPathItem(String $ref, RefFormat refFormat) {

        final PathItem pathItem = cache.loadRef($ref, refFormat, PathItem.class);

        String newRef;

        Map paths = openAPI.getPaths();

        if (paths == null) {
            paths = new LinkedHashMap<>();
        }

        final String possiblyConflictingDefinitionName = computeDefinitionName($ref);

        PathItem existingPathItem = paths.get("/"+possiblyConflictingDefinitionName);

        if (existingPathItem != null) {
            LOGGER.debug("A model for " + existingPathItem + " already exists");
            if(existingPathItem.get$ref() != null) {
                // use the new model
                existingPathItem = null;
            }else {
                return pathItem;
            }
        }

        newRef = possiblyConflictingDefinitionName;
        cache.putRenamedRef($ref, newRef);

        if(pathItem != null) {
            processPathItem(pathItem, $ref);
        }

        return pathItem;
    }

    private void processPathItem(PathItem pathItem, String $ref) {
        if(pathItem.readOperationsMap() != null) {
            final Map operationMap = pathItem.readOperationsMap();
            for (PathItem.HttpMethod httpMethod : operationMap.keySet()) {
                Operation operation = operationMap.get(httpMethod);
                if (operation.getResponses() != null) {
                    final Map responses = operation.getResponses();
                    if (responses != null) {
                        for (String responseCode : responses.keySet()) {
                            ApiResponse response = responses.get(responseCode);
                            if (response != null) {
                                Schema schema = null;
                                if (response.getContent() != null) {
                                    Map content = response.getContent();
                                    for (String mediaName : content.keySet()) {
                                        MediaType mediaType = content.get(mediaName);
                                        if (mediaType.getSchema() != null) {
                                            schema = mediaType.getSchema();
                                            if (schema != null) {
                                                processRefSchemaObject(mediaType.getSchema(), $ref);
                                            }
                                            if (mediaType.getExamples() != null) {
                                                processRefExamples(mediaType.getExamples(), $ref);
                                            }

                                        }
                                    }
                                }
                                if (response.getLinks() != null) {
                                    processRefLinks(response.getLinks(), $ref);
                                }
                                if (response.getHeaders() != null) {
                                    processRefHeaders(response.getHeaders(), $ref);
                                }
                            }
                        }
                    }
                }
                if (operation.getRequestBody() != null) {
                    RequestBody body = operation.getRequestBody();
                    if (body.getContent() != null) {
                        Schema schema;
                        Map content = body.getContent();
                        for (String mediaName : content.keySet()) {
                            MediaType mediaType = content.get(mediaName);
                            if (mediaType.getSchema() != null) {
                                schema = mediaType.getSchema();
                                if (schema != null) {
                                    processRefSchemaObject(mediaType.getSchema(), $ref);
                                }
                            }
                        }
                    }
                }

                final List parameters = operation.getParameters();
                if (parameters != null) {
                    parameters.stream()
                        .filter(parameter -> parameter.getSchema() != null)
                        .forEach(parameter -> this.processRefSchemaObject(parameter.getSchema(), $ref));
                }
            }
        }
    }

    private void processDiscriminator(Discriminator d, String file) {
        if (d != null && d.getMapping() != null) {
            processDiscriminatorMapping(d.getMapping(), file);
        }
    }

    private void processDiscriminatorMapping(Map mapping, String file) {
        for (String key : mapping.keySet()) {
            String ref = mapping.get(key);
            Schema subtype = new Schema().$ref(ref);
            processSchema(subtype, file);
            mapping.put(key, subtype.get$ref());
        }

    }

    public String processRefToExternalResponse(String $ref, RefFormat refFormat) {
        String renamedRef = cache.getRenamedRef($ref);
        if(renamedRef != null) {
            return renamedRef;
        }
        final ApiResponse response = cache.loadRef($ref, refFormat, ApiResponse.class);

        String newRef;

        if (openAPI.getComponents() == null) {
            openAPI.setComponents(new Components());
        }
        Map responses = openAPI.getComponents().getResponses();

        if (responses == null) {
            responses = new LinkedHashMap<>();
        }

        final String possiblyConflictingDefinitionName = computeDefinitionName($ref);

        ApiResponse existingResponse = responses.get(possiblyConflictingDefinitionName);

        if (existingResponse != null) {
            LOGGER.debug("A model for " + existingResponse + " already exists");
            if(existingResponse.get$ref() != null) {
                // use the new model
                existingResponse = null;
            }
        }
        newRef = possiblyConflictingDefinitionName;
        cache.putRenamedRef($ref, newRef);

        if(existingResponse == null) {
            // don't overwrite existing model reference
            openAPI.getComponents().addResponses(newRef, response);
            cache.addReferencedKey(newRef);

            String file = $ref.split("#/")[0];
            if (response.get$ref() != null) {
                RefFormat format = computeRefFormat(response.get$ref());
                if (isAnExternalRefFormat(format)) {
                    String fullRef = response.get$ref();
                    if (!format.equals(RefFormat.URL)) {
                        String parent = file.substring(0, file.lastIndexOf('/'));
                        if (!parent.isEmpty()) {
                            if (fullRef.contains("#/")) {
                                String[] parts = fullRef.split("#/");
                                String fullRefFilePart = parts[0];
                                String fullRefInternalRefPart = parts[1];
                                fullRef = Paths.get(parent, fullRefFilePart).normalize().toString() + "#/" + fullRefInternalRefPart;
                            } else {
                                fullRef = Paths.get(parent, fullRef).normalize().toString();
                            }
                        }

                    }
                    response.set$ref(processRefToExternalResponse(fullRef, format));
                } else {
                    processRefToExternalResponse(file + response.get$ref(), RefFormat.RELATIVE);
                }
            }
        }

        if(response != null) {
            if(response.getContent() != null){
                processRefContent(response.getContent(), $ref);
            }
            if(response.getHeaders() != null){
                processRefHeaders(response.getHeaders(), $ref);
            }
            if(response.getLinks() != null){
                processRefLinks(response.getLinks(), $ref);
            }
        }

        return newRef;
    }


    public String processRefToExternalRequestBody(String $ref, RefFormat refFormat) {
        String renamedRef = cache.getRenamedRef($ref);
        if(renamedRef != null) {
            return renamedRef;
        }

        final RequestBody body = cache.loadRef($ref, refFormat, RequestBody.class);

        if(body == null) {
            // stop!  There's a problem.  retain the original ref
            LOGGER.warn("unable to load model reference from `" + $ref + "`.  It may not be available " +
                    "or the reference isn't a valid model schema");
            return $ref;
        }
        String newRef;

        if (openAPI.getComponents() == null) {
            openAPI.setComponents(new Components());
        }
        Map bodies = openAPI.getComponents().getRequestBodies();

        if (bodies == null) {
            bodies = new LinkedHashMap<>();
        }

        final String possiblyConflictingDefinitionName = computeDefinitionName($ref);

        RequestBody existingBody= bodies.get(possiblyConflictingDefinitionName);

        if (existingBody != null) {
            LOGGER.debug("A model for " + existingBody + " already exists");
            if(existingBody.get$ref() != null) {
                // use the new model
                existingBody = null;
            }
        }
        newRef = possiblyConflictingDefinitionName;
        cache.putRenamedRef($ref, newRef);

        if(existingBody == null) {
            // don't overwrite existing model reference
            openAPI.getComponents().addRequestBodies(newRef, body);
            cache.addReferencedKey(newRef);

            String file = $ref.split("#/")[0];
            if (body.get$ref() != null) {
                RefFormat format = computeRefFormat(body.get$ref());
                if (isAnExternalRefFormat(format)) {
                    body.set$ref(processRefToExternalRequestBody(body.get$ref(), format));
                } else {
                    processRefToExternalRequestBody(file + body.get$ref(), RefFormat.RELATIVE);
                }
            }else if(body.getContent() != null){
                processRefContent(body.getContent(), $ref);
            }
        }

        return newRef;
    }

    public String processRefToExternalHeader(String $ref, RefFormat refFormat) {
        String renamedRef = cache.getRenamedRef($ref);
        if(renamedRef != null) {
            return renamedRef;
        }

        final Header header = cache.loadRef($ref, refFormat, Header.class);

        if(header == null) {
            // stop!  There's a problem.  retain the original ref
            LOGGER.warn("unable to load model reference from `" + $ref + "`.  It may not be available " +
                    "or the reference isn't a valid model schema");
            return $ref;
        }
        String newRef;

        if (openAPI.getComponents() == null) {
            openAPI.setComponents(new Components());
        }
        Map headers = openAPI.getComponents().getHeaders();

        if (headers == null) {
            headers = new LinkedHashMap<>();
        }

        final String possiblyConflictingDefinitionName = computeDefinitionName($ref);

        Header existingHeader = headers.get(possiblyConflictingDefinitionName);

        if (existingHeader != null) {
            LOGGER.debug("A model for " + existingHeader + " already exists");
            if(existingHeader.get$ref() != null) {
                // use the new model
                existingHeader = null;
            }
        }
        newRef = possiblyConflictingDefinitionName;
        cache.putRenamedRef($ref, newRef);

        if(existingHeader == null) {
            // don't overwrite existing model reference
            openAPI.getComponents().addHeaders(newRef, header);
            cache.addReferencedKey(newRef);

            String file = $ref.split("#/")[0];
            if (header.get$ref() != null) {
                RefFormat format = computeRefFormat(header.get$ref());
                if (isAnExternalRefFormat(format)) {
                    header.set$ref(processRefToExternalHeader(header.get$ref(), format));
                } else {
                    processRefToExternalHeader(file + header.get$ref(), RefFormat.RELATIVE);
                }
            }
        }

        if(header != null) {
            if(header.getContent() != null){
                processRefContent(header.getContent(), $ref);
            }
            if(header.getSchema() != null){
                processRefSchemaObject(header.getSchema(), $ref);
            }
        }

        return newRef;
    }

    public String processRefToExternalSecurityScheme(String $ref, RefFormat refFormat) {
        String renamedRef = cache.getRenamedRef($ref);
        if(renamedRef != null) {
            return renamedRef;
        }

        final SecurityScheme securityScheme = cache.loadRef($ref, refFormat, SecurityScheme.class);

        if(securityScheme == null) {
            // stop!  There's a problem.  retain the original ref
            LOGGER.warn("unable to load model reference from `" + $ref + "`.  It may not be available " +
                    "or the reference isn't a valid model schema");
            return $ref;
        }
        String newRef;

        if (openAPI.getComponents() == null) {
            openAPI.setComponents(new Components());
        }
        Map securitySchemeMap = openAPI.getComponents().getSecuritySchemes();

        if (securitySchemeMap == null) {
            securitySchemeMap = new LinkedHashMap<>();
        }

        final String possiblyConflictingDefinitionName = computeDefinitionName($ref);

        SecurityScheme existingSecurityScheme = securitySchemeMap.get(possiblyConflictingDefinitionName);

        if (existingSecurityScheme != null) {
            LOGGER.debug("A model for " + existingSecurityScheme + " already exists");
            if(existingSecurityScheme.get$ref() != null) {
                // use the new model
                existingSecurityScheme = null;
            }
        }
        newRef = possiblyConflictingDefinitionName;
        cache.putRenamedRef($ref, newRef);

        if(existingSecurityScheme == null) {
            // don't overwrite existing model reference
            openAPI.getComponents().addSecuritySchemes(newRef, securityScheme);
            cache.addReferencedKey(newRef);

            String file = $ref.split("#/")[0];
            if (securityScheme.get$ref() != null) {
                RefFormat format = computeRefFormat(securityScheme.get$ref());
                if (isAnExternalRefFormat(format)) {
                    securityScheme.set$ref(processRefToExternalSecurityScheme(securityScheme.get$ref(), format));
                } else {
                    processRefToExternalSecurityScheme(file + securityScheme.get$ref(), RefFormat.RELATIVE);
                }
            }
        }

        return newRef;
    }

    public String processRefToExternalLink(String $ref, RefFormat refFormat) {
        String renamedRef = cache.getRenamedRef($ref);
        if(renamedRef != null) {
            return renamedRef;
        }

        final Link link = cache.loadRef($ref, refFormat, Link.class);

        if(link == null) {
            // stop!  There's a problem.  retain the original ref
            LOGGER.warn("unable to load model reference from `" + $ref + "`.  It may not be available " +
                    "or the reference isn't a valid model schema");
            return $ref;
        }
        String newRef;

        if (openAPI.getComponents() == null) {
            openAPI.setComponents(new Components());
        }
        Map links = openAPI.getComponents().getLinks();

        if (links == null) {
            links = new LinkedHashMap<>();
        }

        final String possiblyConflictingDefinitionName = computeDefinitionName($ref);

        Link existingLink = links.get(possiblyConflictingDefinitionName);

        if (existingLink != null) {
            LOGGER.debug("A model for " + existingLink + " already exists");
            if(existingLink.get$ref() != null) {
                // use the new model
                existingLink = null;
            }
        }
        newRef = possiblyConflictingDefinitionName;
        cache.putRenamedRef($ref, newRef);

        if(existingLink == null) {
            // don't overwrite existing model reference
            openAPI.getComponents().addLinks(newRef, link);
            cache.addReferencedKey(newRef);

            String file = $ref.split("#/")[0];
            if (link.get$ref() != null) {
                RefFormat format = computeRefFormat(link.get$ref());
                if (isAnExternalRefFormat(format)) {
                    link.set$ref(processRefToExternalLink(link.get$ref(), format));
                } else {
                    processRefToExternalLink(file + link.get$ref(), RefFormat.RELATIVE);
                }
            }
        }

        return newRef;
    }

    public String processRefToExternalExample(String $ref, RefFormat refFormat) {
        String renamedRef = cache.getRenamedRef($ref);
        if(renamedRef != null) {
            return renamedRef;
        }

        final Example example = cache.loadRef($ref, refFormat, Example.class);

        if(example == null) {
            // stop!  There's a problem.  retain the original ref
            LOGGER.warn("unable to load model reference from `" + $ref + "`.  It may not be available " +
                    "or the reference isn't a valid model schema");
            return $ref;
        }
        String newRef;

        if (openAPI.getComponents() == null) {
            openAPI.setComponents(new Components());
        }
        Map examples = openAPI.getComponents().getExamples();

        if (examples == null) {
            examples = new LinkedHashMap<>();
        }

        final String possiblyConflictingDefinitionName = computeDefinitionName($ref);

        Example existingExample = examples.get(possiblyConflictingDefinitionName);

        if (existingExample != null) {
            LOGGER.debug("A model for " + existingExample + " already exists");
            if(existingExample.get$ref() != null) {
                // use the new model
                existingExample = null;
            }
        }
        newRef = possiblyConflictingDefinitionName;
        cache.putRenamedRef($ref, newRef);

        if(existingExample == null) {
            // don't overwrite existing model reference
            openAPI.getComponents().addExamples(newRef, example);
            cache.addReferencedKey(newRef);

            String file = $ref.split("#/")[0];
            if (example.get$ref() != null) {
                RefFormat format = computeRefFormat(example.get$ref());
                if (isAnExternalRefFormat(format)) {
                    example.set$ref(processRefToExternalExample(example.get$ref(), format));
                } else {
                    processRefToExternalExample(file + example.get$ref(), RefFormat.RELATIVE);
                }
            }
        }

        return newRef;
    }

    public String processRefToExternalParameter(String $ref, RefFormat refFormat) {
        String renamedRef = cache.getRenamedRef($ref);
        if(renamedRef != null) {
            return renamedRef;
        }

        final Parameter parameter = cache.loadRef($ref, refFormat, Parameter.class);

        if(parameter == null) {
            // stop!  There's a problem.  retain the original ref
            LOGGER.warn("unable to load model reference from `" + $ref + "`.  It may not be available " +
                    "or the reference isn't a valid model schema");
            return $ref;
        }
        String newRef;

        if (openAPI.getComponents() == null) {
            openAPI.setComponents(new Components());
        }
        Map parameters = openAPI.getComponents().getParameters();

        if (parameters == null) {
            parameters = new LinkedHashMap<>();
        }

        final String possiblyConflictingDefinitionName = computeDefinitionName($ref);

        Parameter existingParameters = parameters.get(possiblyConflictingDefinitionName);

        if (existingParameters != null) {
            LOGGER.debug("A model for " + existingParameters + " already exists");
            if(existingParameters.get$ref() != null) {
                // use the new model
                existingParameters = null;
            }
        }
        newRef = possiblyConflictingDefinitionName;
        cache.putRenamedRef($ref, newRef);

        if(existingParameters == null) {
            // don't overwrite existing model reference
            openAPI.getComponents().addParameters(newRef, parameter);
            cache.addReferencedKey(newRef);

            String file = $ref.split("#/")[0];
            if (parameter.get$ref() != null) {
                RefFormat format = computeRefFormat(parameter.get$ref());
                if (isAnExternalRefFormat(format)) {
                    String fullRef = parameter.get$ref();
                    if (!format.equals(RefFormat.URL)) {
                        String parent = file.substring(0, file.lastIndexOf('/'));
                        if (!parent.isEmpty()) {
                            if (fullRef.contains("#/")) {
                                String[] parts = fullRef.split("#/");
                                String fullRefFilePart = parts[0];
                                String fullRefInternalRefPart = parts[1];
                                fullRef = Paths.get(parent, fullRefFilePart).normalize().toString() + "#/" + fullRefInternalRefPart;
                            } else {
                                fullRef = Paths.get(parent, fullRef).normalize().toString();
                            }
                        }

                    }
                    parameter.set$ref(processRefToExternalParameter(fullRef, format));
                } else {
                    processRefToExternalParameter(file + parameter.get$ref(), RefFormat.RELATIVE);
                }
            }
        }

        if(parameter != null) {
            if(parameter.getContent() != null){
                processRefContent(parameter.getContent(), $ref);
            }
            if(parameter.getSchema() != null){
                processRefSchemaObject(parameter.getSchema(), $ref);
            }
            if(parameter.getExamples() != null) {
               processRefExamples(parameter.getExamples(), $ref);
            }
        }

        return newRef;
    }

    public String processRefToExternalCallback(String $ref, RefFormat refFormat) {
        String renamedRef = cache.getRenamedRef($ref);
        if(renamedRef != null) {
            return renamedRef;
        }

        final Callback callback = cache.loadRef($ref, refFormat, Callback.class);

        if(callback == null) {
            // stop!  There's a problem.  retain the original ref
            LOGGER.warn("unable to load model reference from `" + $ref + "`.  It may not be available " +
                    "or the reference isn't a valid model schema");
            return $ref;
        }
        String newRef;

        if (openAPI.getComponents() == null) {
            openAPI.setComponents(new Components());
        }
        Map callbacks = openAPI.getComponents().getCallbacks();

        if (callbacks == null) {
            callbacks = new LinkedHashMap<>();
        }

        final String possiblyConflictingDefinitionName = computeDefinitionName($ref);

        Callback existingCallback = callbacks.get(possiblyConflictingDefinitionName);

        if (existingCallback != null) {
            LOGGER.debug("A model for " + existingCallback + " already exists");
            if(existingCallback.get$ref() != null) {
                // use the new model
                existingCallback = null;
            }
        }
        newRef = possiblyConflictingDefinitionName;
        cache.putRenamedRef($ref, newRef);

        if(existingCallback == null) {
            // don't overwrite existing model reference
            openAPI.getComponents().addCallbacks(newRef, callback);
            cache.addReferencedKey(newRef);

            String file = $ref.split("#/")[0];
            if(callback.get$ref() != null){
                if (callback.get$ref() != null) {
                    RefFormat format = computeRefFormat(callback.get$ref());
                    if (isAnExternalRefFormat(format)) {
                        callback.set$ref(processRefToExternalCallback(callback.get$ref(), format));
                    } else {
                        processRefToExternalCallback(file + callback.get$ref(), RefFormat.RELATIVE);
                    }
                }
            } else {
                for (String path : callback.keySet()) {
                    PathItem pathItem = callback.get(path);
                    if(pathItem != null) {
                        if (pathItem.get$ref() != null) {
                            RefFormat pathRefFormat = computeRefFormat(pathItem.get$ref());
                            String path$ref = pathItem.get$ref();
                            if (isAnExternalRefFormat(refFormat)) {
                                pathItem = this.processRefToExternalPathItem(path$ref, pathRefFormat);
                            } else {
                                pathItem = cache.loadRef(pathItem.get$ref(), refFormat, PathItem.class);
                            }
                            callback.put(path, pathItem);
                        } else {
                            this.processPathItem(pathItem, $ref);
                        }
                    }
                }
            }
        }

        return newRef;
    }


    private void processRefContent(Map content, String $ref) {
        for(MediaType mediaType : content.values()) {
            if(mediaType.getSchema() != null) {
                processRefSchemaObject(mediaType.getSchema(), $ref);
            }
            if(mediaType.getExamples() != null) {
                processRefExamples(mediaType.getExamples(), $ref);
            }
        }
    }

    private void processRefExamples(Map examples, String $ref) {
        String file = $ref.split("#/")[0];
        for(Example example : examples.values()) {
            if (example.get$ref() != null) {
                RefFormat ref = computeRefFormat(example.get$ref());
                if (isAnExternalRefFormat(ref)) {
                    processRefExample(example, $ref);
                } else {
                    processRefToExternalExample(file + example.get$ref(), RefFormat.RELATIVE);
                }
            }
        }
    }

    private void processRefExample(Example example, String externalFile) {
        RefFormat format = computeRefFormat(example.get$ref());

        if (!isAnExternalRefFormat(format)) {
            example.set$ref(RefType.SCHEMAS.getInternalPrefix()+ processRefToExternalSchema(externalFile + example.get$ref(), RefFormat.RELATIVE));
            return;
        }
        String $ref = example.get$ref();
        String subRefExternalPath = getExternalPath(example.get$ref())
                .orElse(null);

        if (format.equals(RefFormat.RELATIVE) && !Objects.equals(subRefExternalPath, externalFile)) {
            $ref = join(externalFile, example.get$ref());
            example.set$ref($ref);
        }else {
            processRefToExternalExample($ref, format);
        }
    }

    private void processRefSchemaObject(Schema schema, String $ref) {
        String file = $ref.split("#/")[0];
        if (schema.get$ref() != null) {
            RefFormat ref = computeRefFormat(schema.get$ref());
            if (isAnExternalRefFormat(ref)) {
                processRefSchema(schema, file);
            } else {
                processRefToExternalSchema(file + schema.get$ref(), RefFormat.RELATIVE);
            }
        }else{
            processSchema(schema,file);
        }
    }

    private void processRefHeaders(Map headers, String $ref) {
        String file = $ref.split("#/")[0];
        for(Header header : headers.values()) {
            if (header.get$ref() != null) {
                RefFormat ref = computeRefFormat(header.get$ref());
                if (isAnExternalRefFormat(ref)) {
                    processRefHeader(header, $ref);
                } else {
                    processRefToExternalHeader(file + header.get$ref(), RefFormat.RELATIVE);
                }
            }
        }
    }

    private void processRefLinks(Map links, String $ref) {
        String file = $ref.split("#/")[0];
        for(Link link : links.values()) {
            if (link.get$ref() != null) {
                RefFormat ref = computeRefFormat(link.get$ref());
                if (isAnExternalRefFormat(ref)) {
                    processRefLink(link, $ref);
                } else {
                    processRefToExternalLink(file + link.get$ref(), RefFormat.RELATIVE);
                }
            }
        }
    }



    private void processRefSchema(Schema subRef, String externalFile) {
        RefFormat format = computeRefFormat(subRef.get$ref());

        if (!isAnExternalRefFormat(format)) {
            subRef.set$ref(RefType.SCHEMAS.getInternalPrefix()+ processRefToExternalSchema(externalFile + subRef.get$ref(), RefFormat.RELATIVE));
            return;
        }
        String $ref = subRef.get$ref();
        String subRefExternalPath = getExternalPath(subRef.get$ref())
            .orElse(null);

        if (format.equals(RefFormat.RELATIVE) && !Objects.equals(subRefExternalPath, externalFile)) {
            $ref = constructRef(subRef, externalFile);
            subRef.set$ref($ref);
        }else {
            processRefToExternalSchema($ref, format);
        }
    }


    protected String constructRef(Schema refProperty, String rootLocation) {
        String ref = refProperty.get$ref();
        return join(rootLocation, ref);
    }

    private void processRefHeader(Header subRef, String externalFile) {
        RefFormat format = computeRefFormat(subRef.get$ref());

        if (!isAnExternalRefFormat(format)) {
            subRef.set$ref(RefType.SCHEMAS.getInternalPrefix()+ processRefToExternalSchema(externalFile + subRef.get$ref(), RefFormat.RELATIVE));
            return;
        }
        String $ref = subRef.get$ref();
        String subRefExternalPath = getExternalPath(subRef.get$ref())
                .orElse(null);

        if (format.equals(RefFormat.RELATIVE) && !Objects.equals(subRefExternalPath, externalFile)) {
            $ref = join(externalFile, subRef.get$ref());
            subRef.set$ref($ref);
        }else {
            processRefToExternalHeader($ref, format);
        }
    }

    private void processRefLink(Link subRef, String externalFile) {
        RefFormat format = computeRefFormat(subRef.get$ref());

        if (!isAnExternalRefFormat(format)) {
            subRef.set$ref(RefType.SCHEMAS.getInternalPrefix()+ processRefToExternalSchema(externalFile + subRef.get$ref(), RefFormat.RELATIVE));
            return;
        }
        String $ref = subRef.get$ref();
        String subRefExternalPath = getExternalPath(subRef.get$ref())
                .orElse(null);

        if (format.equals(RefFormat.RELATIVE) && !Objects.equals(subRefExternalPath, externalFile)) {
            $ref = join(externalFile, subRef.get$ref());
            subRef.set$ref($ref);
        }else {
            processRefToExternalLink($ref, format);
        }
    }


    // visible for testing
    public static String join(String source, String fragment) {
        try {
            boolean isRelative = false;
            if(source.startsWith("/") || source.startsWith(".")) {
                isRelative = true;
            }
            URI uri = new URI(source);

            if(!source.endsWith("/") && (fragment.startsWith("./") && "".equals(uri.getPath()))) {
                uri = new URI(source + "/");
            }
            else if("".equals(uri.getPath()) && !fragment.startsWith("/")) {
                uri = new URI(source + "/");
            }
            URI f = new URI(fragment);

            URI resolved = uri.resolve(f);

            URI normalized = resolved.normalize();
            if(Character.isAlphabetic(normalized.toString().charAt(0)) && isRelative) {
                return "./" + normalized.toString();
            }
            return normalized.toString();
        }
        catch(Exception e) {
            return source;
        }
    }



}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy