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