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