Skip to content

Commit

Permalink
fix: finalize documentation of open api
Browse files Browse the repository at this point in the history
  • Loading branch information
Thorsten Schlathoelter authored and bbortt committed Jan 30, 2025
1 parent 2a8ca5d commit 5a036af
Show file tree
Hide file tree
Showing 57 changed files with 1,466 additions and 537 deletions.
6 changes: 0 additions & 6 deletions connectors/citrus-openapi/README.md

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,15 @@
package org.citrusframework.openapi;

import static java.lang.String.format;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.synchronizedList;
import static org.citrusframework.openapi.OpenApiSettings.isNeglectBasePathGlobally;
import static org.citrusframework.openapi.OpenApiSettings.isRequestValidationEnabledGlobally;
import static org.citrusframework.openapi.OpenApiSettings.isResponseValidationEnabledGlobally;

import jakarta.annotation.Nullable;
import jakarta.validation.constraints.NotNull;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.citrusframework.exceptions.CitrusRuntimeException;
import org.citrusframework.openapi.validation.OpenApiValidationPolicy;
import org.citrusframework.repository.BaseRepository;
Expand Down Expand Up @@ -83,50 +77,7 @@ public OpenApiRepository() {
super(DEFAULT_NAME);
}

/**
* @param openApiResource the OpenAPI resource from which to determine the alias
* @return an {@code Optional} containing the resource alias if it can be resolved, otherwise an
* empty {@code Optional}
*/
// Package protection for testing
static Optional<String> determineResourceAlias(Resource openApiResource) {
String resourceAlias = null;

try {
File file = openApiResource.getFile();
if (file != null) {
resourceAlias = file.getName();
int index = resourceAlias.lastIndexOf(".");
if (index != -1 && index != resourceAlias.length() - 1) {
resourceAlias = resourceAlias.substring(0, index);
}
return Optional.of(resourceAlias);
}
} catch (Exception e) {
// Ignore and try with url
}

try {
URL url = openApiResource.getURL();
if (url != null) {
String urlString = URLDecoder.decode(url.getPath(), UTF_8).replace("\\", "/");
int index = urlString.lastIndexOf("/");
resourceAlias = urlString;
if (index != -1 && index != urlString.length() - 1) {
resourceAlias = resourceAlias.substring(index + 1);
}
index = resourceAlias.lastIndexOf(".");
if (index != -1 && index != resourceAlias.length() - 1) {
resourceAlias = resourceAlias.substring(0, index);
}

}
} catch (MalformedURLException e) {
logger.error("Unable to determine resource alias from resource!", e);
}

return Optional.ofNullable(resourceAlias);
}

public List<OpenApiSpecification> getOpenApiSpecifications() {
return openApiSpecifications;
Expand Down Expand Up @@ -210,7 +161,6 @@ public void addRepository(Resource openApiResource) {
try {
OpenApiSpecification openApiSpecification = OpenApiSpecification.from(openApiResource,
validationPolicy);
determineResourceAlias(openApiResource).ifPresent(openApiSpecification::addAlias);
openApiSpecification.setApiRequestValidationEnabled(requestValidationEnabled);
openApiSpecification.setApiResponseValidationEnabled(responseValidationEnabled);
openApiSpecification.setRootContextPath(rootContextPath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@
package org.citrusframework.openapi;

import static java.lang.String.format;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.emptySet;
import static java.util.Collections.singletonList;
import static java.util.Collections.synchronizedSet;
import static org.citrusframework.openapi.OpenApiSettings.getRequestAutoFillRandomValues;
import static org.citrusframework.openapi.OpenApiSettings.getResponseAutoFillRandomValues;
import static org.citrusframework.openapi.OpenApiSettings.isGenerateOptionalFieldsGlobally;
import static org.citrusframework.openapi.OpenApiSettings.isNeglectBasePathGlobally;
import static org.citrusframework.openapi.OpenApiSettings.isRequestValidationEnabledGlobally;
Expand All @@ -29,13 +32,16 @@
import static org.citrusframework.util.StringUtils.hasText;
import static org.citrusframework.util.StringUtils.isEmpty;

import io.apicurio.datamodels.core.models.Extension;
import io.apicurio.datamodels.core.models.common.Info;
import io.apicurio.datamodels.openapi.models.OasDocument;
import io.apicurio.datamodels.openapi.models.OasOperation;
import io.apicurio.datamodels.openapi.models.OasPathItem;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
Expand Down Expand Up @@ -86,8 +92,9 @@ public class OpenApiSpecification {
private static final String HTTP = "http";

/**
* A unique identifier (UID) for this specification at runtime. The UID is generated based on the SHA
* of the OpenAPI document combined with the full context path to which the API is attached.
* A unique identifier (UID) for this specification at runtime. The UID is generated based on
* the SHA of the OpenAPI document combined with the full context path to which the API is
* attached.
*
* @see OpenApiSpecification#determineUid for detailed information on how the UID is generated.
*/
Expand Down Expand Up @@ -145,8 +152,22 @@ public class OpenApiSpecification {

private OasDocument openApiDoc;
private OpenApiValidationContext openApiValidationContext;

/**
* Generate optional attributes when generating random schema objects.
*/
private boolean generateOptionalFields = isGenerateOptionalFieldsGlobally();

/**
* Autofill parameters and body of request with random data.
*/
private AutoFillType requestAutoFill = getRequestAutoFillRandomValues();

/**
* Autofill parameters and body of response with random data.
*/
private AutoFillType responseAutoFill = getResponseAutoFillRandomValues();

/**
* Flag to indicate, whether request validation is enabled on api level. Api level overrules
* global level and may be overruled by request level.
Expand Down Expand Up @@ -174,7 +195,8 @@ public OpenApiSpecification(OpenApiValidationPolicy openApiValidationPolicy) {
}

/**
* Creates an OpenAPI specification instance from the given URL applying the default validation policy.
* Creates an OpenAPI specification instance from the given URL applying the default validation
* policy.
*
* @param specUrl the URL pointing to the OpenAPI specification to load
* @return an OpenApiSpecification instance populated with the document and validation context
Expand All @@ -196,13 +218,20 @@ public static OpenApiSpecification from(String specUrl,
OpenApiSpecification specification = new OpenApiSpecification(openApiValidationPolicy);
specification.setSpecUrl(specUrl);

try {
determineUrlAlias(new URL(specUrl)).ifPresent(specification::addAlias);
} catch (MalformedURLException e) {
// Ignore;
}

return specification;
}

/**
* Creates an OpenAPI specification instance from the given URL applying the default validation policy.
* Creates an OpenAPI specification instance from the given URL applying the default validation
* policy.
*
* @param specUrl the URL pointing to the OpenAPI specification to load
* @param specUrl the URL pointing to the OpenAPI specification to load
* @return an OpenApiSpecification instance populated with the document and validation context
*/
public static OpenApiSpecification from(URL specUrl) {
Expand All @@ -227,9 +256,12 @@ public static OpenApiSpecification from(URL specUrl,
openApiDoc = OpenApiResourceLoader.fromWebResource(specUrl);
}

openApiValidationContext = OpenApiValidationContextLoader.fromSpec(OasModelHelper.toJson(openApiDoc), openApiValidationPolicy);
openApiValidationContext = OpenApiValidationContextLoader.fromSpec(
OasModelHelper.toJson(openApiDoc), openApiValidationPolicy);
specification.setOpenApiValidationContext(openApiValidationContext);

determineUrlAlias(specUrl).ifPresent(specification::addAlias);

specification.setSpecUrl(specUrl.toString());
specification.initPathLookups();
specification.setOpenApiDoc(openApiDoc);
Expand Down Expand Up @@ -267,9 +299,12 @@ public static OpenApiSpecification from(Resource resource,
OasDocument openApiDoc = OpenApiResourceLoader.fromFile(resource);

specification.setOpenApiValidationContext(
OpenApiValidationContextLoader.fromSpec(OasModelHelper.toJson(openApiDoc), openApiValidationPolicy));
OpenApiValidationContextLoader.fromSpec(OasModelHelper.toJson(openApiDoc),
openApiValidationPolicy));
specification.setOpenApiDoc(openApiDoc);

determineResourceAlias(resource).ifPresent(specification::addAlias);

String schemeToUse = Optional.ofNullable(OasModelHelper.getSchemes(openApiDoc))
.orElse(singletonList(HTTP))
.stream()
Expand Down Expand Up @@ -377,7 +412,6 @@ public synchronized OasDocument getOpenApiDoc(TestContext context) {
Resource resource = Resources.create(resolvedSpecUrl);
initApiDoc(() -> OpenApiResourceLoader.fromFile(resource));


if (requestUrl == null) {
String schemeToUse = Optional.ofNullable(OasModelHelper.getSchemes(openApiDoc))
.orElse(singletonList(HTTP))
Expand Down Expand Up @@ -550,6 +584,32 @@ public void setGenerateOptionalFields(boolean generateOptionalFields) {
this.generateOptionalFields = generateOptionalFields;
}

public AutoFillType getRequestAutoFill() {
return requestAutoFill;
}

public void setRequestAutoFill(AutoFillType requestAutoFill) {
this.requestAutoFill = requestAutoFill;
}

public OpenApiSpecification requestAutoFill(AutoFillType requestAutoFill) {
setRequestAutoFill(requestAutoFill);
return this;
}

public AutoFillType getResponseAutoFill() {
return responseAutoFill;
}

public void setResponseAutoFill(AutoFillType responseAutoFill) {
this.responseAutoFill = responseAutoFill;
}

public OpenApiSpecification responseAutoFill(AutoFillType responseAutoFill) {
setResponseAutoFill(responseAutoFill);
return this;
}

public String getRootContextPath() {
return rootContextPath;
}
Expand Down Expand Up @@ -605,6 +665,11 @@ private Collection<String> collectAliases(OasDocument document) {
}
}

Extension xAlias = info.getExtension("x-citrus-alias");
if (xAlias != null && xAlias.value != null) {
set.add(xAlias.value.toString());
}

return set;
}

Expand Down Expand Up @@ -643,27 +708,26 @@ public void initOpenApiDoc(TestContext context) {
*/
public String getFullPath(OasPathItem oasPathItem) {
return appendSegmentToUrlPath(rootContextPath,
getFullBasePath(oasPathItem));
getFullBasePath(oasPathItem));
}

/**
* Get the full base-path for the given {@link OasPathItem}.
* <p>
* The full base-path is constructed by concatenating the base path (if
* applicable), and the path of the given {@code oasPathItem}. The resulting format is:
* The full base-path is constructed by concatenating the base path (if applicable), and the
* path of the given {@code oasPathItem}. The resulting format is:
* </p>
* <pre>
* /basePath/pathItemPath
* </pre>
* If the base path is to be neglected, it is excluded from the final constructed path.
*
* @param oasPathItem the OpenAPI path item whose full base-path is to be constructed
* @return the full base URL path, consisting of the base path, and the given path
* item
* @return the full base URL path, consisting of the base path, and the given path item
*/
public String getFullBasePath(OasPathItem oasPathItem) {
return appendSegmentToUrlPath(
getApplicableBasePath(), oasPathItem.getPath());
getApplicableBasePath(), oasPathItem.getPath());
}


Expand Down Expand Up @@ -721,4 +785,58 @@ public OpenApiSpecification alias(String alias) {
addAlias(alias);
return this;
}

/**
* @param openApiResource the OpenAPI resource from which to determine the alias
* @return an {@code Optional} containing the resource alias if it can be resolved, otherwise an
* empty {@code Optional}
*/
// Package protection for testing
static Optional<String> determineResourceAlias(Resource openApiResource) {
String resourceAlias = null;

try {
File file = openApiResource.getFile();
if (file != null) {
resourceAlias = file.getName();
int index = resourceAlias.lastIndexOf(".");
if (index != -1 && index != resourceAlias.length() - 1) {
resourceAlias = resourceAlias.substring(0, index);
}
return Optional.of(resourceAlias);
}
} catch (Exception e) {
// Ignore and try with url
}

try {
URL url = openApiResource.getURL();
return determineUrlAlias(url);
} catch (MalformedURLException e) {
logger.error("Unable to determine resource alias from resource!", e);
}

return Optional.ofNullable(resourceAlias);
}

static Optional<String> determineUrlAlias(URL url) {
String resourceAlias = null;

if (url != null) {
String urlString = URLDecoder.decode(url.getPath(), UTF_8).replace("\\", "/");
int index = urlString.lastIndexOf("/");
resourceAlias = urlString;
if (index != -1 && index != urlString.length() - 1) {
resourceAlias = resourceAlias.substring(index + 1);
}
index = resourceAlias.lastIndexOf(".");
if (index != -1 && index != resourceAlias.length() - 1) {
resourceAlias = resourceAlias.substring(0, index);
}

}

return Optional.ofNullable(resourceAlias);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import org.citrusframework.http.message.HttpMessageBuilder;
import org.citrusframework.message.Message;
import org.citrusframework.openapi.AutoFillType;
import org.citrusframework.openapi.OpenApiSettings;
import org.citrusframework.openapi.OpenApiSpecification;
import org.citrusframework.openapi.model.OasModelHelper;
import org.citrusframework.openapi.model.OperationPathAdapter;
Expand Down Expand Up @@ -129,8 +128,7 @@ public static class OpenApiClientRequestMessageBuilder extends HttpMessageBuilde

private final String operationId;

// TODO: document me
private AutoFillType autoFill = OpenApiSettings.getRequestAutoFillRandomValues();
private AutoFillType autoFill ;

public OpenApiClientRequestMessageBuilder(HttpMessage httpMessage,
OpenApiSpecificationSource openApiSpec,
Expand All @@ -149,6 +147,11 @@ public OpenApiClientRequestMessageBuilder autoFill(AutoFillType autoFill) {
public Message build(TestContext context, String messageType) {
OpenApiSpecification openApiSpecification = openApiSpecificationSource.resolve(
context.getReferenceResolver());

if (autoFill == null) {
autoFill = openApiSpecification.getRequestAutoFill();
}

openApiSpecification.initOpenApiDoc(context);
openApiSpecification.getOperation(operationId, context)
.ifPresentOrElse(operationPathAdapter ->
Expand Down
Loading

0 comments on commit 5a036af

Please sign in to comment.