001package org.hl7.fhir.r4b.openapi; 002 003/* 004 Copyright (c) 2011+, HL7, Inc. 005 All rights reserved. 006 007 Redistribution and use in source and binary forms, with or without modification, 008 are permitted provided that the following conditions are met: 009 010 * Redistributions of source code must retain the above copyright notice, this 011 list of conditions and the following disclaimer. 012 * Redistributions in binary form must reproduce the above copyright notice, 013 this list of conditions and the following disclaimer in the documentation 014 and/or other materials provided with the distribution. 015 * Neither the name of HL7 nor the names of its contributors may be used to 016 endorse or promote products derived from this software without specific 017 prior written permission. 018 019 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 022 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 023 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 024 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 025 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 026 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 027 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 028 POSSIBILITY OF SUCH DAMAGE. 029 030 */ 031 032 033import java.util.HashSet; 034import java.util.List; 035import java.util.Set; 036 037import org.hl7.fhir.r4b.context.IWorkerContext; 038import org.hl7.fhir.r4b.model.CapabilityStatement; 039import org.hl7.fhir.r4b.model.CapabilityStatement.CapabilityStatementRestComponent; 040import org.hl7.fhir.r4b.model.CapabilityStatement.CapabilityStatementRestResourceComponent; 041import org.hl7.fhir.r4b.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent; 042import org.hl7.fhir.r4b.model.CapabilityStatement.ResourceInteractionComponent; 043import org.hl7.fhir.r4b.model.CapabilityStatement.ResourceVersionPolicy; 044import org.hl7.fhir.r4b.model.CapabilityStatement.SystemInteractionComponent; 045import org.hl7.fhir.r4b.model.CapabilityStatement.SystemRestfulInteraction; 046import org.hl7.fhir.r4b.model.CapabilityStatement.TypeRestfulInteraction; 047import org.hl7.fhir.r4b.model.CodeType; 048import org.hl7.fhir.r4b.model.ContactDetail; 049import org.hl7.fhir.r4b.model.ContactPoint; 050import org.hl7.fhir.r4b.model.ContactPoint.ContactPointSystem; 051import org.hl7.fhir.r4b.model.Enumerations.SearchParamType; 052import org.hl7.fhir.r4b.model.SearchParameter; 053import org.hl7.fhir.r4b.openapi.ParameterWriter.ParameterLocation; 054import org.hl7.fhir.r4b.openapi.ParameterWriter.ParameterStyle; 055import org.hl7.fhir.r4b.openapi.SchemaWriter.SchemaType; 056import org.hl7.fhir.utilities.Utilities; 057 058 059public class OpenApiGenerator { 060 061 private IWorkerContext context; 062 private CapabilityStatement source; 063 private Writer dest; 064 065 public OpenApiGenerator(IWorkerContext context, CapabilityStatement cs, Writer oa) { 066 this.context = context; 067 this.source = cs; 068 this.dest = oa; 069 } 070 071 public void generate(String license, String url) { 072 dest.info().title(source.present()).description(source.getDescription()).license(license, url).version(source.getVersion()); 073 for (ContactDetail cd : source.getContact()) { 074 dest.info().contact(cd.getName(), email(cd.getTelecom()), url(cd.getTelecom())); 075 } 076 if (source.hasPublisher()) 077 dest.info().contact(source.getPublisher(), null, null); 078 079 if (source.hasImplementation()) { 080 dest.server(source.getImplementation().getUrl()).description(source.getImplementation().getDescription()); 081 } 082 dest.externalDocs().url(source.getUrl()).description("FHIR CapabilityStatement"); 083 084 for (CapabilityStatementRestComponent csr : source.getRest()) { 085 if (csr.getMode() == org.hl7.fhir.r4b.model.CapabilityStatement.RestfulCapabilityMode.SERVER) { 086 generatePaths(csr); 087 } 088 } 089 writeBaseParameters(dest.components()); 090 } 091 092 private void writeBaseParameters(ComponentsWriter components) { 093 components.parameter("rid").name("rid").in(ParameterLocation.path).description("id of the resource (=Resource.id)").required(true).allowEmptyValue(false).style(ParameterStyle.simple) 094 .schema().type(SchemaType.string); 095 096 components.parameter("hid").name("hid").in(ParameterLocation.path).description("id of the history entry (=Resource.meta.versionId)").required(true).allowEmptyValue(false).style(ParameterStyle.simple) 097 .schema().type(SchemaType.string); 098 099 components.parameter("summary").name("_summary").in(ParameterLocation.query).description("Requests the server to return a designated subset of the resource").allowEmptyValue().style(ParameterStyle.form) 100 .schema().type(SchemaType.string).enums("true", "text", "data", "count", "false"); 101 102 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.form) 103 .schema().type(SchemaType.string).format("mime-type"); 104 105 components.parameter("pretty").name("_pretty").in(ParameterLocation.query).description("Ask for a pretty printed response for human convenience").allowEmptyValue().style(ParameterStyle.form) 106 .schema().type(SchemaType.bool); 107 108 SchemaWriter p = components.parameter("elements").name("_elements").in(ParameterLocation.query).description("Requests the server to return a collection of elements from the resource").allowEmptyValue().style(ParameterStyle.form).explode(false) 109 .schema(); 110 p.type(SchemaType.array).format("string"); 111 p.items().format("string"); 112 113 components.parameter("count").name("_count").in(ParameterLocation.query).description("The maximum number of search results on a page. The server is not bound to return the number requested, but cannot return more") 114 .schema().type(SchemaType.number); 115 } 116 117 private void generatePaths(CapabilityStatementRestComponent csr) { 118 generateMetadata(); 119 for (CapabilityStatementRestResourceComponent r : csr.getResource()) 120 generateResource(r); 121 if (hasOp(csr, SystemRestfulInteraction.HISTORYSYSTEM)) 122 generateHistorySystem(csr); 123 if (hasOp(csr, SystemRestfulInteraction.SEARCHSYSTEM)) 124 generateSearchSystem(csr); 125 if (hasOp(csr, SystemRestfulInteraction.BATCH) || hasOp(csr, SystemRestfulInteraction.TRANSACTION) ) 126 generateBatchTransaction(csr); 127 } 128 129 private void generateResource(CapabilityStatementRestResourceComponent r) { 130 if (hasOp(r, TypeRestfulInteraction.SEARCHTYPE)) 131 generateSearch(r); 132 if (hasOp(r, TypeRestfulInteraction.READ)) 133 generateRead(r); 134 if (hasOp(r, TypeRestfulInteraction.CREATE)) 135 generateCreate(r); 136 if (hasOp(r, TypeRestfulInteraction.UPDATE)) 137 generateUpdate(r); 138 if (hasOp(r, TypeRestfulInteraction.PATCH)) 139 generatePatch(r); 140 if (hasOp(r, TypeRestfulInteraction.DELETE)) 141 generateDelete(r); 142 if (hasOp(r, TypeRestfulInteraction.HISTORYINSTANCE)) 143 generateHistoryInstance(r); 144 if (hasOp(r, TypeRestfulInteraction.VREAD)) 145 generateVRead(r); 146 if (hasOp(r, TypeRestfulInteraction.HISTORYTYPE)) 147 generateHistoryType(r); 148 } 149 150 private void generateMetadata() { 151 OperationWriter op = makePathMetadata().operation("get"); 152 op.summary("Return the server's capability statement"); 153 op.operationId("metadata"); 154 opOutcome(op.responses().defaultResponse()); 155 ResponseObjectWriter resp = op.responses().httpResponse("200"); 156 resp.description("the capbility statement"); 157 if (isJson()) 158 resp.content("application/fhir+json").schemaRef(specRef()+"/fhir.schema.json#/definitions/CapabilityStatement"); 159 if (isXml()) 160 resp.content("application/fhir+xml").schemaRef(specRef()+"/CapabilityStatement.xsd"); 161 162 // parameters - but do they apply? 163 op.paramRef("#/components/parameters/format"); 164 op.paramRef("#/components/parameters/pretty"); 165 op.paramRef("#/components/parameters/summary"); 166 op.paramRef("#/components/parameters/elements"); 167 } 168 169 private void generateRead(CapabilityStatementRestResourceComponent r) { 170 OperationWriter op = makePathResId(r).operation("get"); 171 op.summary("Read the current state of the resource"); 172 op.operationId("read"+r.getType()); 173 opOutcome(op.responses().defaultResponse()); 174 ResponseObjectWriter resp = op.responses().httpResponse("200"); 175 resp.description("the resource being returned"); 176 if (r.getVersioning() != ResourceVersionPolicy.NOVERSION) 177 resp.header("ETag").description("Version from Resource.meta.version as a weak ETag").schema().type(SchemaType.string); 178 if (isJson()) 179 resp.content("application/fhir+json").schemaRef(specRef()+"/fhir.schema.json#/definitions/"+r.getType()); 180 if (isXml()) 181 resp.content("application/fhir+xml").schemaRef(specRef()+"/"+r.getType()+".xsd"); 182 183 // parameters: 184 op.paramRef("#/components/parameters/rid"); 185 op.paramRef("#/components/parameters/summary"); 186 op.paramRef("#/components/parameters/format"); 187 op.paramRef("#/components/parameters/pretty"); 188 op.paramRef("#/components/parameters/elements"); 189 } 190 191 private void generateSearch(CapabilityStatementRestResourceComponent r) { 192 OperationWriter op = makePathResType(r).operation("get"); 193 op.summary("Search all resources of type "+r.getType()+" based on a set of criteria"); 194 op.operationId("search"+r.getType()); 195 opOutcome(op.responses().defaultResponse()); 196 ResponseObjectWriter resp = op.responses().httpResponse("200"); 197 resp.description("the resource being returned"); 198 if (isJson()) 199 resp.content("application/fhir+json").schemaRef(specRef()+"/fhir.schema.json#/definitions/Bundle"); 200 if (isXml()) 201 resp.content("application/fhir+xml").schemaRef(specRef()+"/Bundle.xsd"); 202 // todo: how do we know that these apply? 203 op.paramRef("#/components/parameters/format"); 204 op.paramRef("#/components/parameters/pretty"); 205 op.paramRef("#/components/parameters/summary"); 206 op.paramRef("#/components/parameters/elements"); 207 Set<String> set = new HashSet<>(); 208 for (CapabilityStatementRestResourceSearchParamComponent spc : r.getSearchParam()) { 209 if (!set.contains(spc.getName())) { 210 set.add(spc.getName()); 211 ParameterWriter p = op.parameter(spc.getName()); 212 p.in(ParameterLocation.query).description(spc.getDocumentation()); 213 p.schema().type(getSchemaType(spc.getType())); 214 if (spc.hasDefinition()) { 215 SearchParameter sp = context.fetchResource(SearchParameter.class, spc.getDefinition()); 216 if (sp != null) { 217 p.description(sp.getDescription()); 218 } 219 } 220 } 221 } 222 } 223 224 private void generateSearchSystem(CapabilityStatementRestComponent csr) { 225 OperationWriter op = makePathSystem().operation("get"); 226 op.summary("Search all resources of all types based on a set of criteria"); 227 op.operationId("searchAll"); 228 opOutcome(op.responses().defaultResponse()); 229 ResponseObjectWriter resp = op.responses().httpResponse("200"); 230 resp.description("the resource being returned"); 231 if (isJson()) 232 resp.content("application/fhir+json").schemaRef(specRef()+"/fhir.schema.json#/definitions/Bundle"); 233 if (isXml()) 234 resp.content("application/fhir+xml").schemaRef(specRef()+"/Bundle.xsd"); 235 // todo: how do we know that these apply? 236 op.paramRef("#/components/parameters/format"); 237 op.paramRef("#/components/parameters/pretty"); 238 op.paramRef("#/components/parameters/summary"); 239 op.paramRef("#/components/parameters/elements"); 240 Set<String> set = new HashSet<>(); 241 set.add("_summary"); 242 set.add("_format"); 243 set.add("_pretty"); 244 set.add("_elements"); 245 for (CapabilityStatementRestResourceSearchParamComponent spc : csr.getSearchParam()) { 246 if (!set.contains(spc.getName())) { 247 set.add(spc.getName()); 248 ParameterWriter p = op.parameter(spc.getName()); 249 p.in(ParameterLocation.query).description(spc.getDocumentation()); 250 p.schema().type(getSchemaType(spc.getType())); 251 if (spc.hasDefinition()) { 252 SearchParameter sp = context.fetchResource(SearchParameter.class, spc.getDefinition()); 253 if (sp != null) { 254 p.description(sp.getDescription()); 255 } 256 } 257 } 258 } 259 } 260 261 private SchemaType getSchemaType(SearchParamType type) { 262 switch (type) { 263 // case COMPOSITE: 264 case DATE: return SchemaType.dateTime; 265 case NUMBER: return SchemaType.number; 266 case QUANTITY: return SchemaType.string; 267 case REFERENCE: return SchemaType.string; 268 case STRING: return SchemaType.string; 269 case TOKEN: return SchemaType.string; 270 case URI: return SchemaType.string; 271 } 272 return null; 273 } 274 275 private void generateHistoryType(CapabilityStatementRestResourceComponent r) { 276 OperationWriter op = makePathResHistListType(r).operation("get"); 277 op.summary("Read the past states of the resource"); 278 op.operationId("histtype"+r.getType()); 279 opOutcome(op.responses().defaultResponse()); 280 ResponseObjectWriter resp = op.responses().httpResponse("200"); 281 resp.description("the resources being returned"); 282 if (isJson()) 283 resp.content("application/fhir+json").schemaRef(specRef()+"/fhir.schema.json#/definitions/Bundle"); 284 if (isXml()) 285 resp.content("application/fhir+xml").schemaRef(specRef()+"/Bundle.xsd"); 286 op.paramRef("#/components/parameters/summary"); 287 op.paramRef("#/components/parameters/format"); 288 op.paramRef("#/components/parameters/pretty"); 289 op.paramRef("#/components/parameters/elements"); 290 op.paramRef("#/components/parameters/count"); 291 292 op.parameter("_since").in(ParameterLocation.query).description("Only include resource versions that were created at or after the given instant in time").schema().type(SchemaType.dateTime); 293 op.parameter("_at").in(ParameterLocation.query).description("Only include resource versions that were current at some point during the time period specified in the date time value (see Search notes on date searching)").schema().type(SchemaType.dateTime); 294 op.parameter("_list").in(ParameterLocation.query).description("Only include resource versions that are referenced in the specified list (current list references are allowed)").schema().type(SchemaType.string); 295 } 296 297 private void generateHistoryInstance(CapabilityStatementRestResourceComponent r) { 298 OperationWriter op = makePathResHistListId(r).operation("get"); 299 op.summary("Read the past states of the resource"); 300 op.operationId("histinst"+r.getType()); 301 opOutcome(op.responses().defaultResponse()); 302 ResponseObjectWriter resp = op.responses().httpResponse("200"); 303 resp.description("the resources being returned"); 304 if (isJson()) 305 resp.content("application/fhir+json").schemaRef(specRef()+"/fhir.schema.json#/definitions/Bundle"); 306 if (isXml()) 307 resp.content("application/fhir+xml").schemaRef(specRef()+"/Bundle.xsd"); 308 op.paramRef("#/components/parameters/rid"); 309 op.paramRef("#/components/parameters/summary"); 310 op.paramRef("#/components/parameters/format"); 311 op.paramRef("#/components/parameters/pretty"); 312 op.paramRef("#/components/parameters/elements"); 313 op.paramRef("#/components/parameters/count"); 314 315 op.parameter("_since").in(ParameterLocation.query).description("Only include resource versions that were created at or after the given instant in time").schema().type(SchemaType.dateTime); 316 op.parameter("_at").in(ParameterLocation.query).description("Only include resource versions that were current at some point during the time period specified in the date time value (see Search notes on date searching)").schema().type(SchemaType.dateTime); 317 op.parameter("_list").in(ParameterLocation.query).description("Only include resource versions that are referenced in the specified list (current list references are allowed)").schema().type(SchemaType.string); 318 } 319 320 private void generateHistorySystem(CapabilityStatementRestComponent csr) { 321 OperationWriter op = makePathHistListSystem().operation("get"); 322 op.summary("Read the past states of all resources"); 323 op.operationId("histAll"); 324 opOutcome(op.responses().defaultResponse()); 325 ResponseObjectWriter resp = op.responses().httpResponse("200"); 326 resp.description("the resources being returned"); 327 if (isJson()) 328 resp.content("application/fhir+json").schemaRef(specRef()+"/fhir.schema.json#/definitions/Bundle"); 329 if (isXml()) 330 resp.content("application/fhir+xml").schemaRef(specRef()+"/Bundle.xsd"); 331 op.paramRef("#/components/parameters/summary"); 332 op.paramRef("#/components/parameters/format"); 333 op.paramRef("#/components/parameters/pretty"); 334 op.paramRef("#/components/parameters/elements"); 335 op.paramRef("#/components/parameters/count"); 336 337 op.parameter("_since").in(ParameterLocation.query).description("Only include resource versions that were created at or after the given instant in time").schema().type(SchemaType.dateTime); 338 op.parameter("_at").in(ParameterLocation.query).description("Only include resource versions that were current at some point during the time period specified in the date time value (see Search notes on date searching)").schema().type(SchemaType.dateTime); 339 op.parameter("_list").in(ParameterLocation.query).description("Only include resource versions that are referenced in the specified list (current list references are allowed)").schema().type(SchemaType.string); 340 } 341 342 private void generateVRead(CapabilityStatementRestResourceComponent r) { 343 OperationWriter op = makePathResHistId(r).operation("get"); 344 op.summary("Read a past state of the resource"); 345 op.operationId("vread"+r.getType()); 346 opOutcome(op.responses().defaultResponse()); 347 ResponseObjectWriter resp = op.responses().httpResponse("200"); 348 resp.description("the resource being returned"); 349 if (r.getVersioning() != ResourceVersionPolicy.NOVERSION) 350 resp.header("ETag").description("Version from Resource.meta.version as a weak ETag for that version").schema().type(SchemaType.string); 351 if (isJson()) 352 resp.content("application/fhir+json").schemaRef(specRef()+"/fhir.schema.json#/definitions/"+r.getType()); 353 if (isXml()) 354 resp.content("application/fhir+xml").schemaRef(specRef()+"/"+r.getType()+".xsd"); 355 op.paramRef("#/components/parameters/rid"); 356 op.paramRef("#/components/parameters/hid"); 357 op.paramRef("#/components/parameters/summary"); 358 op.paramRef("#/components/parameters/format"); 359 op.paramRef("#/components/parameters/pretty"); 360 op.paramRef("#/components/parameters/elements"); 361 } 362 363 // todo: how does prefer header affect return type? 364 private void generateUpdate(CapabilityStatementRestResourceComponent r) { 365 OperationWriter op = makePathResId(r).operation("put"); 366 if (r.getUpdateCreate()) 367 op.summary("Update the current state of the resource (can create a new resource if it does not exist)"); 368 else 369 op.summary("Update the current state of the resource"); 370 op.operationId("update"+r.getType()); 371 RequestBodyWriter req = op.request(); 372 req.description("The new state of the resource").required(true); 373 if (isJson()) 374 req.content("application/fhir+json").schemaRef(specRef()+"/fhir.schema.json#/definitions/"+r.getType()); 375 if (isXml()) 376 req.content("application/fhir+xml").schemaRef(specRef()+"/"+r.getType()+".xsd"); 377 378 opOutcome(op.responses().defaultResponse()); 379 ResponseObjectWriter resp = op.responses().httpResponse("200"); 380 resp.description("the resource being returned after being updated"); 381 if (r.getVersioning() != ResourceVersionPolicy.NOVERSION) 382 resp.header("ETag").description("Version from Resource.meta.version as a weak ETag").schema().type(SchemaType.string); 383 if (isJson()) 384 resp.content("application/fhir+json").schemaRef(specRef()+"/fhir.schema.json#/definitions/"+r.getType()); 385 if (isXml()) 386 resp.content("application/fhir+xml").schemaRef(specRef()+"/"+r.getType()+".xsd"); 387 op.paramRef("#/components/parameters/rid"); 388 op.paramRef("#/components/parameters/summary"); 389 op.paramRef("#/components/parameters/format"); 390 op.paramRef("#/components/parameters/pretty"); 391 op.paramRef("#/components/parameters/elements"); 392 } 393 394 private void generatePatch(CapabilityStatementRestResourceComponent r) { 395 OperationWriter op = makePathResId(r).operation("patch"); 396 op.summary("Change the current state of the resource by providing a patch - a series of change commands"); 397 op.operationId("patch"+r.getType()); 398 RequestBodyWriter req = op.request(); 399 req.description("The new state of the resource").required(true); 400 if (isJson()) { 401 req.content("application/json-patch+json").schemaRef(specRef()+"/fhir.schema.json#/definitions/"+r.getType()); 402 req.content("application/fhir+json").schemaRef(specRef()+"/fhir.schema.json#/definitions/Parameters"); 403 } 404 if (isXml()) { 405 req.content("application/xml-patch+xml").schemaRef(specRef()+"/"+r.getType()+".xsd"); 406 req.content("application/fhir+xml").schemaRef(specRef()+"/Parameters.xsd"); 407 } 408 409 opOutcome(op.responses().defaultResponse()); 410 ResponseObjectWriter resp = op.responses().httpResponse("200"); 411 resp.description("the resource being returned after being patched"); 412 if (r.getVersioning() != ResourceVersionPolicy.NOVERSION) 413 resp.header("ETag").description("Version from Resource.meta.version as a weak ETag").schema().type(SchemaType.string); 414 if (isJson()) 415 resp.content("application/fhir+json").schemaRef(specRef()+"/fhir.schema.json#/definitions/"+r.getType()); 416 if (isXml()) 417 resp.content("application/fhir+xml").schemaRef(specRef()+"/"+r.getType()+".xsd"); 418 op.paramRef("#/components/parameters/rid"); 419 op.paramRef("#/components/parameters/summary"); 420 op.paramRef("#/components/parameters/format"); 421 op.paramRef("#/components/parameters/pretty"); 422 op.paramRef("#/components/parameters/elements"); 423 } 424 425 private void generateDelete(CapabilityStatementRestResourceComponent r) { 426 OperationWriter op = makePathResId(r).operation("delete"); 427 op.summary("Delete the resource so that it no exists (no read, search etc)"); 428 op.operationId("delete"+r.getType()); 429 opOutcome(op.responses().defaultResponse()); 430 ResponseObjectWriter resp = op.responses().httpResponse("204"); 431 resp.description("If the resource is deleted - no content is returned"); 432 if (r.getVersioning() != ResourceVersionPolicy.NOVERSION) 433 resp.header("ETag").description("Version from Resource.meta.version as a weak ETag").schema().type(SchemaType.string); 434 op.paramRef("#/components/parameters/rid"); 435 } 436 437 private void generateCreate(CapabilityStatementRestResourceComponent r) { 438 OperationWriter op = makePathRes(r).operation("post"); 439 op.summary("Create a new resource"); 440 op.operationId("create"+r.getType()); 441 RequestBodyWriter req = op.request(); 442 req.description("The new state of the resource").required(true); 443 if (isJson()) 444 req.content("application/fhir+json").schemaRef(specRef()+"/fhir.schema.json#/definitions/"+r.getType()); 445 if (isXml()) 446 req.content("application/fhir+xml").schemaRef(specRef()+"/"+r.getType()+".xsd"); 447 448 opOutcome(op.responses().defaultResponse()); 449 ResponseObjectWriter resp = op.responses().httpResponse("200"); 450 resp.description("the resource being returned after being updated"); 451 if (r.getVersioning() != ResourceVersionPolicy.NOVERSION) 452 resp.header("ETag").description("Version from Resource.meta.version as a weak ETag").schema().type(SchemaType.string); 453 if (isJson()) 454 resp.content("application/fhir+json").schemaRef(specRef()+"/fhir.schema.json#/definitions/"+r.getType()); 455 if (isXml()) 456 resp.content("application/fhir+xml").schemaRef(specRef()+"/"+r.getType()+".xsd"); 457 op.paramRef("#/components/parameters/summary"); 458 op.paramRef("#/components/parameters/format"); 459 op.paramRef("#/components/parameters/pretty"); 460 op.paramRef("#/components/parameters/elements"); 461 } 462 463 private void generateBatchTransaction(CapabilityStatementRestComponent csr) { 464 OperationWriter op = makePathSystem().operation("put"); 465 op.summary("Batch or Transaction"); 466 op.operationId("transaction"); 467 RequestBodyWriter req = op.request(); 468 req.description("The batch or transaction").required(true); 469 if (isJson()) 470 req.content("application/fhir+json").schemaRef(specRef()+"/fhir.schema.json#/definitions/Bundle"); 471 if (isXml()) 472 req.content("application/fhir+xml").schemaRef(specRef()+"/Bundle.xsd"); 473 474 opOutcome(op.responses().defaultResponse()); 475 ResponseObjectWriter resp = op.responses().httpResponse("200"); 476 resp.description("Batch or Transaction response"); 477 if (isJson()) 478 resp.content("application/fhir+json").schemaRef(specRef()+"/fhir.schema.json#/definitions/Bundle"); 479 if (isXml()) 480 resp.content("application/fhir+xml").schemaRef(specRef()+"/Bundle.xsd"); 481 op.paramRef("#/components/parameters/format"); 482 op.paramRef("#/components/parameters/pretty"); 483 } 484 485 private void opOutcome(ResponseObjectWriter resp) { 486 resp.description("Error, with details"); 487 if (isJson()) 488 resp.content("application/fhir+json").schemaRef(specRef()+"/fhir.schema.json#/definitions/OperationOutcome"); 489 if (isXml()) 490 resp.content("application/fhir+xml").schemaRef(specRef()+"/OperationOutcome.xsd"); 491 } 492 493 private String specRef() { 494 String ver = context.getVersion(); 495 if (Utilities.noString(ver)) 496 return "https://hl7.org/fhir/STU3"; 497 if (ver.startsWith("4.0")) 498 return "https://hl7.org/fhir/R4"; 499 if (ver.startsWith("3.0")) 500 return "https://hl7.org/fhir/STU3"; 501 if (ver.startsWith("1.0")) 502 return "https://hl7.org/fhir/DSTU2"; 503 if (ver.startsWith("1.4")) 504 return "https://hl7.org/fhir/2016May"; 505 return "https://build.fhir.org"; 506 } 507 508 private boolean isJson() { 509 for (CodeType f : source.getFormat()) { 510 if (f.getCode().contains("json")) 511 return true; 512 } 513 return false; 514 } 515 516 private boolean isXml() { 517 for (CodeType f : source.getFormat()) { 518 if (f.getCode().contains("xml")) 519 return true; 520 } 521 return false; 522 } 523 524 public PathItemWriter makePathSystem() { 525 PathItemWriter p = dest.path("/"); 526 p.summary("System level operations"); 527 p.description("System level operations"); 528 return p; 529 } 530 531 public PathItemWriter makePathMetadata() { 532 PathItemWriter p = dest.path("/metadata"); 533 p.summary("Access to the Server's Capability Statement"); 534 p.description("All FHIR Servers return a CapabilityStatement that describes what services they perform"); 535 return p; 536 } 537 538 public PathItemWriter makePathRes(CapabilityStatementRestResourceComponent r) { 539 PathItemWriter p = dest.path("/"+r.getType()); 540 p.summary("Manager for resources of type "+r.getType()); 541 p.description("The Manager for resources of type "+r.getType()+": provides services to manage the collection of all the "+r.getType()+" instances"); 542 return p; 543 } 544 545 public PathItemWriter makePathResId(CapabilityStatementRestResourceComponent r) { 546 PathItemWriter p = dest.path("/"+r.getType()+"/{rid}"); 547 p.summary("Read/Write/etc resource instance of type "+r.getType()); 548 p.description("Access to services to manage the state of a single resource of type "+r.getType()); 549 return p; 550 } 551 552 public PathItemWriter makePathResType(CapabilityStatementRestResourceComponent r) { 553 PathItemWriter p = dest.path("/"+r.getType()); 554 p.summary("manage the collection of resources of type "+r.getType()); 555 p.description("Access to services to manage the collection of all resources of type "+r.getType()); 556 return p; 557 } 558 559 public PathItemWriter makePathResHistListType(CapabilityStatementRestResourceComponent r) { 560 PathItemWriter p = dest.path("/"+r.getType()+"/_history"); 561 p.summary("Read past versions of resources of type "+r.getType()); 562 p.description("Access to previous versions of resourcez of type "+r.getType()); 563 return p; 564 } 565 566 public PathItemWriter makePathResHistListId(CapabilityStatementRestResourceComponent r) { 567 PathItemWriter p = dest.path("/"+r.getType()+"/{rid}/_history"); 568 p.summary("Read past versions of resource instance of type "+r.getType()); 569 p.description("Access to previous versions of a single resource of type "+r.getType()); 570 return p; 571 } 572 573 public PathItemWriter makePathResHistId(CapabilityStatementRestResourceComponent r) { 574 PathItemWriter p = dest.path("/"+r.getType()+"/{rid}/_history/{hid}"); 575 p.summary("Read a past version of resource instance of type "+r.getType()); 576 p.description("Access a to specified previous version of a single resource of type "+r.getType()); 577 return p; 578 } 579 580 public PathItemWriter makePathHistListSystem() { 581 PathItemWriter p = dest.path("/_history"); 582 p.summary("Read a past version of resource instance of all types"); 583 p.description("Access a previous versions of all types"); 584 return p; 585 } 586 587 private boolean hasOp(CapabilityStatementRestComponent r, SystemRestfulInteraction opCode) { 588 for (SystemInteractionComponent op : r.getInteraction()) { 589 if (op.getCode() == opCode) 590 return true; 591 } 592 return false; 593 } 594 595 private boolean hasOp(CapabilityStatementRestResourceComponent r, TypeRestfulInteraction opCode) { 596 for (ResourceInteractionComponent op : r.getInteraction()) { 597 if (op.getCode() == opCode) 598 return true; 599 } 600 return false; 601 } 602 603 private String url(List<ContactPoint> telecom) { 604 for (ContactPoint cp : telecom) { 605 if (cp.getSystem() == ContactPointSystem.URL) 606 return cp.getValue(); 607 } 608 return null; 609 } 610 611 612 private String email(List<ContactPoint> telecom) { 613 for (ContactPoint cp : telecom) { 614 if (cp.getSystem() == ContactPointSystem.EMAIL) 615 return cp.getValue(); 616 } 617 return null; 618 } 619 620 621 622}