mirror of
https://github.com/hapifhir/hapi-fhir.git
synced 2025-03-09 14:33:32 +00:00
Fix issue that OpenAPI specification had errors when validating it against Swagger/OpenAPI schema and Swagger/OpenAPI spec (#5465)
* Fix issue that OpenAPI specification had errors when validating it against Swagger/OpenAPI schema and Swagger/OpenAPI spec * Add changelog * Consolidate swagger version --------- Co-authored-by: Primož Delopst <primoz.delopst@better.care> Co-authored-by: James Agnew <jamesagnew@gmail.com>
This commit is contained in:
parent
55c3066789
commit
adfdbab0f0
@ -0,0 +1,5 @@
|
||||
---
|
||||
type: fix
|
||||
issue: 5465
|
||||
title: "Several fixes to the HAPI FHIR generated OpenAPI schema have been implemented. This means that
|
||||
the spec now validates cleanly. Thanks to Primož Delopst for the contribution!"
|
@ -35,6 +35,7 @@ import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import ca.uhn.fhir.util.ExtensionConstants;
|
||||
import ca.uhn.fhir.util.HapiExtensions;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.vladsch.flexmark.html.HtmlRenderer;
|
||||
import com.vladsch.flexmark.parser.Parser;
|
||||
import io.swagger.v3.core.util.Yaml;
|
||||
@ -47,9 +48,13 @@ import io.swagger.v3.oas.models.examples.Example;
|
||||
import io.swagger.v3.oas.models.info.Contact;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.media.Content;
|
||||
import io.swagger.v3.oas.models.media.DateSchema;
|
||||
import io.swagger.v3.oas.models.media.DateTimeSchema;
|
||||
import io.swagger.v3.oas.models.media.MediaType;
|
||||
import io.swagger.v3.oas.models.media.NumberSchema;
|
||||
import io.swagger.v3.oas.models.media.ObjectSchema;
|
||||
import io.swagger.v3.oas.models.media.Schema;
|
||||
import io.swagger.v3.oas.models.media.StringSchema;
|
||||
import io.swagger.v3.oas.models.parameters.Parameter;
|
||||
import io.swagger.v3.oas.models.parameters.RequestBody;
|
||||
import io.swagger.v3.oas.models.responses.ApiResponse;
|
||||
@ -73,6 +78,7 @@ import org.hl7.fhir.r4.model.CapabilityStatement;
|
||||
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||
import org.hl7.fhir.r4.model.Coding;
|
||||
import org.hl7.fhir.r4.model.DateType;
|
||||
import org.hl7.fhir.r4.model.Enumerations;
|
||||
import org.hl7.fhir.r4.model.Extension;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.OperationDefinition;
|
||||
@ -109,6 +115,7 @@ import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
@ -679,9 +686,10 @@ public class OpenApiInterceptor {
|
||||
operation.addParametersItem(parametersItem);
|
||||
|
||||
parametersItem.setName(nextSearchParam.getName());
|
||||
parametersItem.setRequired(false);
|
||||
parametersItem.setIn("query");
|
||||
parametersItem.setDescription(nextSearchParam.getDocumentation());
|
||||
parametersItem.setStyle(Parameter.StyleEnum.SIMPLE);
|
||||
parametersItem.setSchema(toSchema(nextSearchParam.getType()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -765,7 +773,13 @@ public class OpenApiInterceptor {
|
||||
"/" + theResourceType + "/$" + operationDefinition.getCode(),
|
||||
PathItem.HttpMethod.GET);
|
||||
populateOperation(
|
||||
theFhirContext, theOpenApi, theResourceType, operationDefinition, operation, true);
|
||||
theFhirContext,
|
||||
theOpenApi,
|
||||
theResourceType,
|
||||
operationDefinition,
|
||||
operation,
|
||||
"/" + theResourceType + "/$" + operationDefinition.getCode(),
|
||||
PathItem.HttpMethod.GET);
|
||||
}
|
||||
if (operationDefinition.getInstance()) {
|
||||
Operation operation = getPathItem(
|
||||
@ -774,13 +788,26 @@ public class OpenApiInterceptor {
|
||||
PathItem.HttpMethod.GET);
|
||||
addResourceIdParameter(operation);
|
||||
populateOperation(
|
||||
theFhirContext, theOpenApi, theResourceType, operationDefinition, operation, true);
|
||||
theFhirContext,
|
||||
theOpenApi,
|
||||
theResourceType,
|
||||
operationDefinition,
|
||||
operation,
|
||||
"/" + theResourceType + "/{id}/$" + operationDefinition.getCode(),
|
||||
PathItem.HttpMethod.GET);
|
||||
}
|
||||
} else {
|
||||
if (operationDefinition.getSystem()) {
|
||||
Operation operation =
|
||||
getPathItem(thePaths, "/$" + operationDefinition.getCode(), PathItem.HttpMethod.GET);
|
||||
populateOperation(theFhirContext, theOpenApi, null, operationDefinition, operation, true);
|
||||
populateOperation(
|
||||
theFhirContext,
|
||||
theOpenApi,
|
||||
null,
|
||||
operationDefinition,
|
||||
operation,
|
||||
"/$" + operationDefinition.getCode(),
|
||||
PathItem.HttpMethod.GET);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -793,7 +820,13 @@ public class OpenApiInterceptor {
|
||||
"/" + theResourceType + "/$" + operationDefinition.getCode(),
|
||||
PathItem.HttpMethod.POST);
|
||||
populateOperation(
|
||||
theFhirContext, theOpenApi, theResourceType, operationDefinition, operation, false);
|
||||
theFhirContext,
|
||||
theOpenApi,
|
||||
theResourceType,
|
||||
operationDefinition,
|
||||
operation,
|
||||
"/" + theResourceType + "/$" + operationDefinition.getCode(),
|
||||
PathItem.HttpMethod.POST);
|
||||
}
|
||||
if (operationDefinition.getInstance()) {
|
||||
Operation operation = getPathItem(
|
||||
@ -802,13 +835,26 @@ public class OpenApiInterceptor {
|
||||
PathItem.HttpMethod.POST);
|
||||
addResourceIdParameter(operation);
|
||||
populateOperation(
|
||||
theFhirContext, theOpenApi, theResourceType, operationDefinition, operation, false);
|
||||
theFhirContext,
|
||||
theOpenApi,
|
||||
theResourceType,
|
||||
operationDefinition,
|
||||
operation,
|
||||
"/" + theResourceType + "/{id}/$" + operationDefinition.getCode(),
|
||||
PathItem.HttpMethod.POST);
|
||||
}
|
||||
} else {
|
||||
if (operationDefinition.getSystem()) {
|
||||
Operation operation =
|
||||
getPathItem(thePaths, "/$" + operationDefinition.getCode(), PathItem.HttpMethod.POST);
|
||||
populateOperation(theFhirContext, theOpenApi, null, operationDefinition, operation, false);
|
||||
populateOperation(
|
||||
theFhirContext,
|
||||
theOpenApi,
|
||||
null,
|
||||
operationDefinition,
|
||||
operation,
|
||||
"/$" + operationDefinition.getCode(),
|
||||
PathItem.HttpMethod.POST);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -847,16 +893,18 @@ public class OpenApiInterceptor {
|
||||
String theResourceType,
|
||||
OperationDefinition theOperationDefinition,
|
||||
Operation theOperation,
|
||||
boolean theGet) {
|
||||
String thePath,
|
||||
PathItem.HttpMethod httpMethod) {
|
||||
if (theResourceType == null) {
|
||||
theOperation.addTagsItem(PAGE_SYSTEM);
|
||||
} else {
|
||||
theOperation.addTagsItem(theResourceType);
|
||||
}
|
||||
theOperation.setSummary(theOperationDefinition.getTitle());
|
||||
theOperation.setSummary(Optional.ofNullable(theOperationDefinition.getTitle())
|
||||
.orElse(String.format("%s: %s", httpMethod.name(), thePath)));
|
||||
theOperation.setDescription(theOperationDefinition.getDescription());
|
||||
addFhirResourceResponse(theFhirContext, theOpenApi, theOperation, null);
|
||||
if (theGet) {
|
||||
if (httpMethod == PathItem.HttpMethod.GET) {
|
||||
|
||||
for (OperationDefinition.OperationDefinitionParameterComponent nextParameter :
|
||||
theOperationDefinition.getParameter()) {
|
||||
@ -873,8 +921,8 @@ public class OpenApiInterceptor {
|
||||
parametersItem.setName(nextParameter.getName());
|
||||
parametersItem.setIn("query");
|
||||
parametersItem.setDescription(nextParameter.getDocumentation());
|
||||
parametersItem.setStyle(Parameter.StyleEnum.SIMPLE);
|
||||
parametersItem.setRequired(nextParameter.getMin() > 0);
|
||||
parametersItem.setSchema(toSchema(nextParameter.getSearchType()));
|
||||
|
||||
List<Extension> exampleExtensions =
|
||||
nextParameter.getExtensionsByUrl(HapiExtensions.EXT_OP_PARAMETER_EXAMPLE_VALUE);
|
||||
@ -1024,6 +1072,7 @@ public class OpenApiInterceptor {
|
||||
parameter.setIn("path");
|
||||
parameter.setDescription("The resource version ID");
|
||||
parameter.setExample("1");
|
||||
parameter.setRequired(true);
|
||||
parameter.setSchema(new Schema().type("string").minimum(new BigDecimal(1)));
|
||||
parameter.setStyle(Parameter.StyleEnum.SIMPLE);
|
||||
theOperation.addParametersItem(parameter);
|
||||
@ -1085,6 +1134,7 @@ public class OpenApiInterceptor {
|
||||
parameter.setIn("path");
|
||||
parameter.setDescription("The resource ID");
|
||||
parameter.setExample("123");
|
||||
parameter.setRequired(true);
|
||||
parameter.setSchema(new Schema().type("string").minimum(new BigDecimal(1)));
|
||||
parameter.setStyle(Parameter.StyleEnum.SIMPLE);
|
||||
theOperation.addParametersItem(parameter);
|
||||
@ -1188,4 +1238,31 @@ public class OpenApiInterceptor {
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private Schema<?> toSchema(Enumerations.SearchParamType type) {
|
||||
if (type == null) {
|
||||
return new StringSchema();
|
||||
}
|
||||
switch (type) {
|
||||
case NUMBER:
|
||||
return new NumberSchema();
|
||||
case DATE:
|
||||
Schema<?> dateSchema = new Schema<>();
|
||||
dateSchema.anyOf(ImmutableList.of(new DateTimeSchema(), new DateSchema()));
|
||||
return dateSchema;
|
||||
case QUANTITY:
|
||||
Schema<?> quantitySchema = new Schema<>();
|
||||
quantitySchema.anyOf(ImmutableList.of(new StringSchema(), new NumberSchema()));
|
||||
return quantitySchema;
|
||||
case STRING:
|
||||
case TOKEN:
|
||||
case REFERENCE:
|
||||
case COMPOSITE:
|
||||
case URI:
|
||||
case SPECIAL:
|
||||
case NULL:
|
||||
default:
|
||||
return new StringSchema();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user