openAPI compliance work + fix for validator
This commit is contained in:
parent
3e9262afe8
commit
a40d30f5cb
|
@ -741,8 +741,7 @@ public abstract class BaseWorkerContext implements IWorkerContext {
|
|||
b.append("\r\n");
|
||||
}
|
||||
}
|
||||
if (res != null)
|
||||
return (T) res;
|
||||
return (T) res;
|
||||
}
|
||||
}
|
||||
if (class_ == CodeSystem.class && codeSystems.containsKey(uri))
|
||||
|
|
|
@ -44,6 +44,17 @@ public class BaseWriter {
|
|||
return arr;
|
||||
}
|
||||
|
||||
protected JsonObject forceArrayObject(String arrayName) {
|
||||
JsonArray arr = object.getAsJsonArray(arrayName);
|
||||
if (arr == null) {
|
||||
arr = new JsonArray();
|
||||
object.add(arrayName, arr);
|
||||
}
|
||||
JsonObject obj = new JsonObject();
|
||||
arr.add(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
protected JsonObject ensureMapObject(String mapName, String value) {
|
||||
JsonObject map = object.getAsJsonObject(mapName);
|
||||
|
|
|
@ -17,4 +17,10 @@ public class ComponentsWriter extends BaseWriter {
|
|||
ensureMapObject("schemas", name).addProperty("$ref", uri);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ParameterWriter parameter(String name) {
|
||||
JsonObject po = new JsonObject();
|
||||
ensureMapObject("parameters", name).add(name, po);
|
||||
return new ParameterWriter(po);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,13 +37,15 @@ public class InfoWriter extends BaseWriter {
|
|||
}
|
||||
|
||||
public InfoWriter contact(String name, String email, String url) {
|
||||
JsonObject person = new JsonObject();
|
||||
person.addProperty("name", name);
|
||||
if (!Utilities.noString(email))
|
||||
person.addProperty("email", email);
|
||||
if (!Utilities.noString(url))
|
||||
person.addProperty("url", url);
|
||||
object.add("contact", person);
|
||||
if (name != null && !object.has("contact")) {
|
||||
JsonObject person = new JsonObject();
|
||||
person.addProperty("name", name);
|
||||
if (!Utilities.noString(email))
|
||||
person.addProperty("email", email);
|
||||
if (!Utilities.noString(url))
|
||||
person.addProperty("url", url);
|
||||
object.add("contact", person);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,11 @@ package org.hl7.fhir.r5.openapi;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestComponent;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceComponent;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement.ResourceInteractionComponent;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement.ResourceVersionPolicy;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement.RestfulCapabilityMode;
|
||||
|
@ -13,24 +15,32 @@ import org.hl7.fhir.r5.model.CodeType;
|
|||
import org.hl7.fhir.r5.model.ContactDetail;
|
||||
import org.hl7.fhir.r5.model.ContactPoint;
|
||||
import org.hl7.fhir.r5.model.ContactPoint.ContactPointSystem;
|
||||
import org.hl7.fhir.r5.model.Enumerations.SearchParamType;
|
||||
import org.hl7.fhir.r5.model.SearchParameter;
|
||||
import org.hl7.fhir.r5.openapi.ParameterWriter.ParameterLocation;
|
||||
import org.hl7.fhir.r5.openapi.ParameterWriter.ParameterStyle;
|
||||
import org.hl7.fhir.r5.openapi.SchemaWriter.SchemaType;
|
||||
|
||||
|
||||
public class OpenApiGenerator {
|
||||
|
||||
private IWorkerContext context;
|
||||
private CapabilityStatement source;
|
||||
private Writer dest;
|
||||
|
||||
public OpenApiGenerator(CapabilityStatement cs, Writer oa) {
|
||||
public OpenApiGenerator(IWorkerContext context, CapabilityStatement cs, Writer oa) {
|
||||
this.context = context;
|
||||
this.source = cs;
|
||||
this.dest = oa;
|
||||
}
|
||||
|
||||
public void generate(String license) {
|
||||
dest.info().title(source.getTitle()).description(source.getDescription()).license(license, null).version(source.getVersion());
|
||||
if (source.hasPublisher())
|
||||
dest.info().contact(source.getPublisher(), null, null);
|
||||
public void generate(String license, String url) {
|
||||
dest.info().title(source.getTitle()).description(source.getDescription()).license(license, url).version(source.getVersion());
|
||||
for (ContactDetail cd : source.getContact()) {
|
||||
dest.info().contact(cd.getName(), email(cd.getTelecom()), url(cd.getTelecom()));
|
||||
}
|
||||
if (source.hasPublisher())
|
||||
dest.info().contact(source.getPublisher(), null, null);
|
||||
|
||||
if (source.hasImplementation()) {
|
||||
dest.server(source.getImplementation().getUrl()).description(source.getImplementation().getDescription());
|
||||
|
@ -42,8 +52,23 @@ public class OpenApiGenerator {
|
|||
generatePaths(csr);
|
||||
}
|
||||
}
|
||||
writeBaseParameters(dest.components());
|
||||
}
|
||||
|
||||
private void writeBaseParameters(ComponentsWriter components) {
|
||||
components.parameter("summary").name("_summary").in(ParameterLocation.query).description("Requests the server to return a designated subset of the resource").allowEmptyValue().style(ParameterStyle.matrix)
|
||||
.schema().type(SchemaType.string).enums("true", "text", "data", "count", "false");
|
||||
|
||||
components.parameter("format").name("_format").in(ParameterLocation.query).description("Specify alternative response formats by their MIME-types (when a client is unable acccess accept: header)").allowEmptyValue().style(ParameterStyle.matrix)
|
||||
.schema().type(SchemaType.string).format("mime-type");
|
||||
|
||||
components.parameter("pretty").name("_pretty").in(ParameterLocation.query).description("Ask for a pretty printed response for human convenience").allowEmptyValue().style(ParameterStyle.matrix)
|
||||
.schema().type(SchemaType.bool);
|
||||
|
||||
components.parameter("elements").name("_elements").in(ParameterLocation.query).description("Requests the server to return a collection of elements from the resource").allowEmptyValue().style(ParameterStyle.matrix).explode(false)
|
||||
.schema().type(SchemaType.array).format("string");
|
||||
}
|
||||
|
||||
private void generatePaths(CapabilityStatementRestComponent csr) {
|
||||
for (CapabilityStatementRestResourceComponent r : csr.getResource())
|
||||
generateResource(r);
|
||||
|
@ -58,6 +83,8 @@ public class OpenApiGenerator {
|
|||
generateVRead(r);
|
||||
if (hasOp(r, TypeRestfulInteraction.UPDATE))
|
||||
generateUpdate(r);
|
||||
if (hasOp(r, TypeRestfulInteraction.CREATE))
|
||||
generateCreate(r);
|
||||
}
|
||||
|
||||
private void generateRead(CapabilityStatementRestResourceComponent r) {
|
||||
|
@ -73,6 +100,12 @@ public class OpenApiGenerator {
|
|||
resp.content("application/fhir+json").schemaRef(specRef()+"/fhir.json.schema#/definitions/"+r.getType());
|
||||
if (isXml())
|
||||
resp.content("application/fhir+xml").schemaRef(specRef()+"/"+r.getType()+".xsd");
|
||||
|
||||
// parameters:
|
||||
op.paramRef("#/Components/parameters/summary");
|
||||
op.paramRef("#/Components/parameters/format");
|
||||
op.paramRef("#/Components/parameters/pretty");
|
||||
op.paramRef("#/Components/parameters/elements");
|
||||
}
|
||||
|
||||
private void generateSearch(CapabilityStatementRestResourceComponent r) {
|
||||
|
@ -88,6 +121,35 @@ public class OpenApiGenerator {
|
|||
resp.content("application/fhir+json").schemaRef(specRef()+"/fhir.json.schema#/definitions/Bundle");
|
||||
if (isXml())
|
||||
resp.content("application/fhir+xml").schemaRef(specRef()+"/Bundle.xsd");
|
||||
op.paramRef("#/Components/parameters/summary");
|
||||
op.paramRef("#/Components/parameters/format");
|
||||
op.paramRef("#/Components/parameters/pretty");
|
||||
op.paramRef("#/Components/parameters/elements");
|
||||
for (CapabilityStatementRestResourceSearchParamComponent spc : r.getSearchParam()) {
|
||||
ParameterWriter p = op.parameter(spc.getName());
|
||||
p.in(ParameterLocation.query).description(spc.getDocumentation());
|
||||
p.schema().type(getSchemaType(spc.getType()));
|
||||
if (spc.hasDefinition()) {
|
||||
SearchParameter sp = context.fetchResource(SearchParameter.class, spc.getDefinition());
|
||||
if (sp != null) {
|
||||
p.description(sp.getDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SchemaType getSchemaType(SearchParamType type) {
|
||||
switch (type) {
|
||||
// case COMPOSITE:
|
||||
case DATE: return SchemaType.dateTime;
|
||||
case NUMBER: return SchemaType.number;
|
||||
case QUANTITY: return SchemaType.string;
|
||||
case REFERENCE: return SchemaType.string;
|
||||
case STRING: return SchemaType.string;
|
||||
case TOKEN: return SchemaType.string;
|
||||
case URI: return SchemaType.string;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void generateVRead(CapabilityStatementRestResourceComponent r) {
|
||||
|
@ -103,12 +165,19 @@ public class OpenApiGenerator {
|
|||
resp.content("application/fhir+json").schemaRef(specRef()+"/fhir.json.schema#/definitions/"+r.getType());
|
||||
if (isXml())
|
||||
resp.content("application/fhir+xml").schemaRef(specRef()+"/"+r.getType()+".xsd");
|
||||
op.paramRef("#/Components/parameters/summary");
|
||||
op.paramRef("#/Components/parameters/format");
|
||||
op.paramRef("#/Components/parameters/pretty");
|
||||
op.paramRef("#/Components/parameters/elements");
|
||||
}
|
||||
|
||||
// todo: how does prefer header affect return type?
|
||||
private void generateUpdate(CapabilityStatementRestResourceComponent r) {
|
||||
OperationWriter op = makePathResId(r).operation("put");
|
||||
op.summary("Update the current state of the resource");
|
||||
if (r.getUpdateCreate())
|
||||
op.summary("Update the current state of the resource (can create a new resource if it does not exist)");
|
||||
else
|
||||
op.summary("Update the current state of the resource");
|
||||
op.operationId("update"+r.getType());
|
||||
RequestBodyWriter req = op.request();
|
||||
req.description("The new state of the resource").required(true);
|
||||
|
@ -126,6 +195,36 @@ public class OpenApiGenerator {
|
|||
resp.content("application/fhir+json").schemaRef(specRef()+"/fhir.json.schema#/definitions/"+r.getType());
|
||||
if (isXml())
|
||||
resp.content("application/fhir+xml").schemaRef(specRef()+"/"+r.getType()+".xsd");
|
||||
op.paramRef("#/Components/parameters/summary");
|
||||
op.paramRef("#/Components/parameters/format");
|
||||
op.paramRef("#/Components/parameters/pretty");
|
||||
op.paramRef("#/Components/parameters/elements");
|
||||
}
|
||||
|
||||
private void generateCreate(CapabilityStatementRestResourceComponent r) {
|
||||
OperationWriter op = makePathRes(r).operation("put");
|
||||
op.summary("Create a new resource");
|
||||
op.operationId("create"+r.getType());
|
||||
RequestBodyWriter req = op.request();
|
||||
req.description("The new state of the resource").required(true);
|
||||
if (isJson())
|
||||
req.content("application/fhir+json").schemaRef(specRef()+"/fhir.json.schema#/definitions/"+r.getType());
|
||||
if (isXml())
|
||||
req.content("application/fhir+xml").schemaRef(specRef()+"/"+r.getType()+".xsd");
|
||||
|
||||
opOutcome(op.responses().defaultResponse());
|
||||
ResponseObjectWriter resp = op.responses().httpResponse("200");
|
||||
resp.description("the resource being returned after being updated");
|
||||
if (r.getVersioning() != ResourceVersionPolicy.NOVERSION)
|
||||
resp.header("ETag").description("Version from Resource.meta.version as a weak ETag");
|
||||
if (isJson())
|
||||
resp.content("application/fhir+json").schemaRef(specRef()+"/fhir.json.schema#/definitions/"+r.getType());
|
||||
if (isXml())
|
||||
resp.content("application/fhir+xml").schemaRef(specRef()+"/"+r.getType()+".xsd");
|
||||
op.paramRef("#/Components/parameters/summary");
|
||||
op.paramRef("#/Components/parameters/format");
|
||||
op.paramRef("#/Components/parameters/pretty");
|
||||
op.paramRef("#/Components/parameters/elements");
|
||||
}
|
||||
|
||||
private void opOutcome(ResponseObjectWriter resp) {
|
||||
|
|
|
@ -39,11 +39,13 @@ public class OperationWriter extends BaseWriter {
|
|||
}
|
||||
|
||||
public ParameterWriter parameter(String name) {
|
||||
return new ParameterWriter(ensureMapObject("parameters", name));
|
||||
JsonObject obj = forceArrayObject("parameters");
|
||||
obj.addProperty("name", name);
|
||||
return new ParameterWriter(obj);
|
||||
}
|
||||
|
||||
public OperationWriter pathRef(String name, String url) {
|
||||
ensureMapObject("parameters", name).addProperty("$ref", url);
|
||||
public OperationWriter paramRef(String url) {
|
||||
forceArrayObject("parameters").addProperty("$ref", url);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,9 +20,19 @@ public class ParameterWriter extends BaseWriter {
|
|||
return this;
|
||||
}
|
||||
|
||||
|
||||
public ParameterWriter name(String value) {
|
||||
object.addProperty("name", value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ParameterWriter allowEmptyValue() {
|
||||
object.addProperty("allowEmptyValue", true);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ParameterWriter description(String value) {
|
||||
object.addProperty("description", value);
|
||||
if (value != null)
|
||||
object.addProperty("description", value);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -64,6 +74,12 @@ public class ParameterWriter extends BaseWriter {
|
|||
return this;
|
||||
}
|
||||
|
||||
public SchemaWriter schema() {
|
||||
JsonObject so = new JsonObject();
|
||||
object.add("schema", so);
|
||||
return new SchemaWriter(so);
|
||||
}
|
||||
|
||||
public ParameterWriter schemaRef(String name, String uri) {
|
||||
JsonObject schema = new JsonObject();
|
||||
schema.addProperty("$ref", uri);
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
package org.hl7.fhir.r5.openapi;
|
||||
|
||||
import org.hl7.fhir.r5.openapi.SchemaWriter.SchemaType;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public class SchemaWriter extends BaseWriter {
|
||||
|
||||
public enum SchemaType {
|
||||
array,
|
||||
bool,
|
||||
dateTime,
|
||||
number,
|
||||
string;
|
||||
|
||||
public String toCode() {
|
||||
switch (this) {
|
||||
case array: return "array";
|
||||
case bool: return "boolean";
|
||||
case dateTime: return "dateTime";
|
||||
case number: return "number";
|
||||
case string: return "string";
|
||||
}
|
||||
return "??";
|
||||
}
|
||||
}
|
||||
|
||||
public SchemaWriter(JsonObject object) {
|
||||
super(object);
|
||||
}
|
||||
|
||||
public SchemaWriter type(SchemaType value) {
|
||||
if (value != null)
|
||||
object.addProperty("type", value.toCode());
|
||||
return this;
|
||||
}
|
||||
|
||||
public SchemaWriter enums(String... values) {
|
||||
JsonArray arr = forceArray("enum");
|
||||
for (String s : values)
|
||||
arr.add(s);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SchemaWriter format(String value) {
|
||||
if (value != null)
|
||||
object.addProperty("format", value);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
|
@ -20,13 +20,13 @@ public class Writer extends BaseWriter {
|
|||
public Writer(OutputStream stream) {
|
||||
super( new JsonObject());
|
||||
this.stream = stream;
|
||||
object.addProperty("openapi", "3.0.1");
|
||||
object.addProperty("openapi", "3.0.2");
|
||||
}
|
||||
|
||||
public Writer(OutputStream stream, InputStream template) throws JsonSyntaxException, IOException {
|
||||
super(parse(template));
|
||||
this.stream = stream;
|
||||
object.addProperty("openapi", "3.0.1");
|
||||
object.addProperty("openapi", "3.0.2");
|
||||
}
|
||||
|
||||
private static JsonObject parse(InputStream template) throws JsonSyntaxException, IOException {
|
||||
|
|
Loading…
Reference in New Issue