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

io.vertx.openapi.contract.OpenAPIContract Maven / Gradle / Ivy

/*
 * Copyright (c) 2023, SAP SE
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
 * which is available at https://www.apache.org/licenses/LICENSE-2.0.
 *
 * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
 *
 */

package io.vertx.openapi.contract;

import io.vertx.codegen.annotations.Nullable;
import io.vertx.codegen.annotations.VertxGen;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.internal.ContextInternal;
import io.vertx.core.json.JsonObject;
import io.vertx.json.schema.JsonSchema;
import io.vertx.json.schema.JsonSchemaValidationException;
import io.vertx.json.schema.SchemaRepository;
import io.vertx.openapi.contract.impl.OpenAPIContractImpl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static io.vertx.core.Future.failedFuture;
import static io.vertx.openapi.contract.OpenAPIContractException.createInvalidContract;
import static io.vertx.openapi.impl.Utils.readYamlOrJson;
import static java.util.Collections.emptyMap;

@VertxGen
public interface OpenAPIContract {

  /**
   * Resolves / dereferences the passed contract and creates an {@link OpenAPIContract} instance.
   *
   * @param vertx                  The related Vert.x instance.
   * @param unresolvedContractPath The path to the unresolved contract.
   * @return A succeeded {@link Future} holding an {@link OpenAPIContract} instance, otherwise a failed {@link Future}.
   */
  static Future from(Vertx vertx, String unresolvedContractPath) {
    return readYamlOrJson(vertx, unresolvedContractPath).compose(json -> from(vertx, json));
  }

  /**
   * Resolves / dereferences the passed contract and creates an {@link OpenAPIContract} instance.
   *
   * @param vertx              The related Vert.x instance.
   * @param unresolvedContract The unresolved contract.
   * @return A succeeded {@link Future} holding an {@link OpenAPIContract} instance, otherwise a failed {@link Future}.
   */
  static Future from(Vertx vertx, JsonObject unresolvedContract) {
    return from(vertx, unresolvedContract, emptyMap());
  }

  /**
   * Resolves / dereferences the passed contract and creates an {@link OpenAPIContract} instance.
   * 

* This method can be used in case that the contract is split into several files. These files can be passed in a * Map that has the reference as key and the path to the file as value. * * @param vertx The related Vert.x instance. * @param unresolvedContractPath The path to the unresolved contract. * @param additionalContractFiles The additional contract files * @return A succeeded {@link Future} holding an {@link OpenAPIContract} instance, otherwise a failed {@link Future}. */ static Future from(Vertx vertx, String unresolvedContractPath, Map additionalContractFiles) { Map> jsonFilesFuture = new HashMap<>(); jsonFilesFuture.put(unresolvedContractPath, readYamlOrJson(vertx, unresolvedContractPath)); additionalContractFiles.forEach((key, value) -> jsonFilesFuture.put(key, readYamlOrJson(vertx, value))); return Future.all(new ArrayList<>(jsonFilesFuture.values())).compose(compFut -> { Map resolvedFiles = new HashMap<>(); additionalContractFiles.keySet().forEach(key -> resolvedFiles.put(key, jsonFilesFuture.get(key).result())); return from(vertx, jsonFilesFuture.get(unresolvedContractPath).result(), resolvedFiles); }); } /** * Resolves / dereferences the passed contract and creates an {@link OpenAPIContract} instance. *

* This method can be used in case that the contract is split into several files. These files can be passed in a * Map that has the reference as key and the path to the file as value. * * @param vertx The related Vert.x instance. * @param unresolvedContract The unresolved contract. * @param additionalContractFiles The additional contract files * @return A succeeded {@link Future} holding an {@link OpenAPIContract} instance, otherwise a failed {@link Future}. */ static Future from(Vertx vertx, JsonObject unresolvedContract, Map additionalContractFiles) { if (unresolvedContract == null) { return failedFuture(createInvalidContract("Spec must not be null")); } OpenAPIVersion version = OpenAPIVersion.fromContract(unresolvedContract); String baseUri = "app://"; ContextInternal ctx = (ContextInternal) vertx.getOrCreateContext(); Promise promise = ctx.promise(); version.getRepository(vertx, baseUri) .compose(repository -> { List> validationFutures = new ArrayList<>(additionalContractFiles.size()); for (String ref : additionalContractFiles.keySet()) { // Todo: As soon a more modern Java version is used the validate part could be extracted in a private static // method and reused below. JsonObject file = additionalContractFiles.get(ref); Future validationFuture = version.validateAdditionalContractFile(vertx, repository, file) .compose(v -> vertx.executeBlocking(() -> repository.dereference(ref, JsonSchema.of(ref, file)))); validationFutures.add(validationFuture); } return Future.all(validationFutures).map(repository); }).compose(repository -> version.validateContract(vertx, repository, unresolvedContract).compose(res -> { try { res.checkValidity(); return version.resolve(vertx, repository, unresolvedContract); } catch (JsonSchemaValidationException | UnsupportedOperationException e) { return failedFuture(createInvalidContract(null, e)); } }) .map(resolvedSpec -> (OpenAPIContract) new OpenAPIContractImpl(resolvedSpec, version, repository)) ).recover(e -> { //Convert any non-openapi exceptions into an OpenAPIContractException if(e instanceof OpenAPIContractException) { return failedFuture(e); } return failedFuture(createInvalidContract("Found issue in specification for reference: " + e.getMessage(), e)); }).onComplete(promise); return promise.future(); } /** * Access to an operation defined in the contract with {@code operationId}. * * @param operationId the id of the operation. * @return the requested operation. * @throws IllegalArgumentException if the operation id doesn't exist in the contract. */ @Nullable Operation operation(String operationId); /** * @return all operations defined in the contract. */ List operations(); /** * @return all {@link Path Paths} defined in the OpenAPI contract. */ List getPaths(); /** * @return the resolved OpenAPI contract as {@link JsonObject}. */ JsonObject getRawContract(); /** * @return the OpenAPI version of the contract. */ OpenAPIVersion getVersion(); /** * @return the {@link SchemaRepository} to validate against. */ SchemaRepository getSchemaRepository(); /** * @return the servers of the contract. */ List getServers(); /** * Finds the related {@link Path} object based on the passed url path. * * @param urlPath The path of the request. * @return the found {@link Path} object, or null if the passed path doesn't match any {@link Path} object. */ @Nullable Path findPath(String urlPath); /** * Finds the related {@link Operation} object based on the passed url path and method. * * @param urlPath The path of the request. * @param method The method of the request. * @return the found {@link Operation} object, or null if the passed path and method doesn't match any * {@link Operation} object. */ @Nullable Operation findOperation(String urlPath, HttpMethod method); /** * Returns the applicable list of global security requirements (scopes) or empty list. * * @return The related security requirement. */ List getSecurityRequirements(); /** * Gets the related {@link SecurityScheme} object based on the passed name. * * @param name The name of the security scheme. * @return the found {@link SecurityScheme} object, or null if the passed path and method doesn't match any * {@link Operation} object. */ @Nullable SecurityScheme securityScheme(String name); }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy