001package org.hl7.fhir.dstu3.hapi.rest.server; 002 003import ca.uhn.fhir.i18n.Msg; 004import ca.uhn.fhir.context.FhirVersionEnum; 005import ca.uhn.fhir.context.RuntimeResourceDefinition; 006import ca.uhn.fhir.context.RuntimeSearchParam; 007import ca.uhn.fhir.parser.DataFormatException; 008import ca.uhn.fhir.rest.annotation.IdParam; 009import ca.uhn.fhir.rest.annotation.Metadata; 010import ca.uhn.fhir.rest.annotation.Read; 011import ca.uhn.fhir.rest.api.Constants; 012import ca.uhn.fhir.rest.api.server.RequestDetails; 013import ca.uhn.fhir.rest.server.*; 014import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; 015import ca.uhn.fhir.rest.server.method.*; 016import ca.uhn.fhir.rest.server.method.OperationMethodBinding.ReturnType; 017import ca.uhn.fhir.rest.server.method.SearchParameter; 018import ca.uhn.fhir.rest.server.util.BaseServerCapabilityStatementProvider; 019import org.apache.commons.lang3.StringUtils; 020import org.hl7.fhir.dstu3.model.*; 021import org.hl7.fhir.dstu3.model.CapabilityStatement.*; 022import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; 023import org.hl7.fhir.dstu3.model.OperationDefinition.OperationDefinitionParameterComponent; 024import org.hl7.fhir.dstu3.model.OperationDefinition.OperationKind; 025import org.hl7.fhir.dstu3.model.OperationDefinition.OperationParameterUse; 026import org.hl7.fhir.exceptions.FHIRException; 027import org.hl7.fhir.instance.model.api.IBaseResource; 028import org.hl7.fhir.instance.model.api.IPrimitiveType; 029 030import javax.servlet.ServletContext; 031import javax.servlet.http.HttpServletRequest; 032import java.util.*; 033import java.util.Map.Entry; 034 035import static org.apache.commons.lang3.StringUtils.isBlank; 036import static org.apache.commons.lang3.StringUtils.isNotBlank; 037 038import ca.uhn.fhir.context.FhirContext; 039 040/* 041 * #%L 042 * HAPI FHIR Structures - DSTU2 (FHIR v1.0.0) 043 * %% 044 * Copyright (C) 2014 - 2015 University Health Network 045 * %% 046 * Licensed under the Apache License, Version 2.0 (the "License"); 047 * you may not use this file except in compliance with the License. 048 * You may obtain a copy of the License at 049 * 050 * http://www.apache.org/licenses/LICENSE-2.0 051 * 052 * Unless required by applicable law or agreed to in writing, software 053 * distributed under the License is distributed on an "AS IS" BASIS, 054 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 055 * See the License for the specific language governing permissions and 056 * limitations under the License. 057 * #L% 058 */ 059 060/** 061 * Server FHIR Provider which serves the conformance statement for a RESTful server implementation 062 * 063 * <p> 064 * Note: This class is safe to extend, but it is important to note that the same instance of {@link CapabilityStatement} is always returned unless {@link #setCache(boolean)} is called with a value of 065 * <code>false</code>. This means that if you are adding anything to the returned conformance instance on each call you should call <code>setCache(false)</code> in your provider constructor. 066 * </p> 067 */ 068public class ServerCapabilityStatementProvider extends BaseServerCapabilityStatementProvider implements IServerConformanceProvider<CapabilityStatement> { 069 070 private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerCapabilityStatementProvider.class); 071 private String myPublisher = "Not provided"; 072 073 /** 074 * No-arg constructor and setter so that the ServerConformanceProvider can be Spring-wired with the RestfulService avoiding the potential reference cycle that would happen. 075 */ 076 public ServerCapabilityStatementProvider() { 077 super(); 078 } 079 080 /** 081 * Constructor 082 * 083 * @deprecated Use no-args constructor instead. Deprecated in 4.0.0 084 */ 085 @Deprecated 086 public ServerCapabilityStatementProvider(RestfulServer theRestfulServer) { 087 this(); 088 } 089 090 /** 091 * Constructor - This is intended only for JAX-RS server 092 */ 093 public ServerCapabilityStatementProvider(RestfulServerConfiguration theServerConfiguration) { 094 super(theServerConfiguration); 095 } 096 097 private void checkBindingForSystemOps(CapabilityStatementRestComponent rest, Set<SystemRestfulInteraction> systemOps, BaseMethodBinding<?> nextMethodBinding) { 098 if (nextMethodBinding.getRestOperationType() != null) { 099 String sysOpCode = nextMethodBinding.getRestOperationType().getCode(); 100 if (sysOpCode != null) { 101 SystemRestfulInteraction sysOp; 102 try { 103 sysOp = SystemRestfulInteraction.fromCode(sysOpCode); 104 } catch (FHIRException e) { 105 return; 106 } 107 if (sysOp == null) { 108 return; 109 } 110 if (systemOps.contains(sysOp) == false) { 111 systemOps.add(sysOp); 112 rest.addInteraction().setCode(sysOp); 113 } 114 } 115 } 116 } 117 118 private Map<String, List<BaseMethodBinding<?>>> collectMethodBindings(RequestDetails theRequestDetails) { 119 Map<String, List<BaseMethodBinding<?>>> resourceToMethods = new TreeMap<>(); 120 for (ResourceBinding next : getServerConfiguration(theRequestDetails).getResourceBindings()) { 121 String resourceName = next.getResourceName(); 122 for (BaseMethodBinding<?> nextMethodBinding : next.getMethodBindings()) { 123 if (resourceToMethods.containsKey(resourceName) == false) { 124 resourceToMethods.put(resourceName, new ArrayList<>()); 125 } 126 resourceToMethods.get(resourceName).add(nextMethodBinding); 127 } 128 } 129 for (BaseMethodBinding<?> nextMethodBinding : getServerConfiguration(theRequestDetails).getServerBindings()) { 130 String resourceName = ""; 131 if (resourceToMethods.containsKey(resourceName) == false) { 132 resourceToMethods.put(resourceName, new ArrayList<>()); 133 } 134 resourceToMethods.get(resourceName).add(nextMethodBinding); 135 } 136 return resourceToMethods; 137 } 138 139 private DateTimeType conformanceDate(RequestDetails theRequestDetails) { 140 IPrimitiveType<Date> buildDate = getServerConfiguration(theRequestDetails).getConformanceDate(); 141 if (buildDate != null && buildDate.getValue() != null) { 142 try { 143 return new DateTimeType(buildDate.getValueAsString()); 144 } catch (DataFormatException e) { 145 // fall through 146 } 147 } 148 return DateTimeType.now(); 149 } 150 151 private String createNamedQueryName(SearchMethodBinding searchMethodBinding) { 152 StringBuilder retVal = new StringBuilder(); 153 if (searchMethodBinding.getResourceName() != null) { 154 retVal.append(searchMethodBinding.getResourceName()); 155 } 156 retVal.append("-query-"); 157 retVal.append(searchMethodBinding.getQueryName()); 158 159 return retVal.toString(); 160 } 161 162 private String createOperationName(OperationMethodBinding theMethodBinding) { 163 StringBuilder retVal = new StringBuilder(); 164 if (theMethodBinding.getResourceName() != null) { 165 retVal.append(theMethodBinding.getResourceName()); 166 } 167 168 retVal.append('-'); 169 if (theMethodBinding.isCanOperateAtInstanceLevel()) { 170 retVal.append('i'); 171 } 172 if (theMethodBinding.isCanOperateAtServerLevel()) { 173 retVal.append('s'); 174 } 175 retVal.append('-'); 176 177 // Exclude the leading $ 178 retVal.append(theMethodBinding.getName(), 1, theMethodBinding.getName().length()); 179 180 return retVal.toString(); 181 } 182 183 /** 184 * Gets the value of the "publisher" that will be placed in the generated conformance statement. As this is a mandatory element, the value should not be null (although this is not enforced). The 185 * value defaults to "Not provided" but may be set to null, which will cause this element to be omitted. 186 */ 187 public String getPublisher() { 188 return myPublisher; 189 } 190 191 /** 192 * Sets the value of the "publisher" that will be placed in the generated conformance statement. As this is a mandatory element, the value should not be null (although this is not enforced). The 193 * value defaults to "Not provided" but may be set to null, which will cause this element to be omitted. 194 */ 195 public void setPublisher(String thePublisher) { 196 myPublisher = thePublisher; 197 } 198 199 200 @SuppressWarnings("EnumSwitchStatementWhichMissesCases") 201 @Override 202 @Metadata 203 public CapabilityStatement getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) { 204 RestfulServerConfiguration serverConfiguration = getServerConfiguration(theRequestDetails); 205 Bindings bindings = serverConfiguration.provideBindings(); 206 207 CapabilityStatement retVal = new CapabilityStatement(); 208 209 retVal.setPublisher(myPublisher); 210 retVal.setDateElement(conformanceDate(theRequestDetails)); 211 retVal.setFhirVersion(FhirVersionEnum.DSTU3.getFhirVersionString()); 212 retVal.setAcceptUnknown(UnknownContentCode.EXTENSIONS); // TODO: make this configurable - this is a fairly big 213 // effort since the parser 214 // needs to be modified to actually allow it 215 216 ServletContext servletContext = (ServletContext) (theRequest == null ? null : theRequest.getAttribute(RestfulServer.SERVLET_CONTEXT_ATTRIBUTE)); 217 String serverBase = serverConfiguration.getServerAddressStrategy().determineServerBase(servletContext, theRequest); 218 retVal 219 .getImplementation() 220 .setUrl(serverBase) 221 .setDescription(serverConfiguration.getImplementationDescription()); 222 223 retVal.setKind(CapabilityStatementKind.INSTANCE); 224 retVal.getSoftware().setName(serverConfiguration.getServerName()); 225 retVal.getSoftware().setVersion(serverConfiguration.getServerVersion()); 226 retVal.addFormat(Constants.CT_FHIR_XML_NEW); 227 retVal.addFormat(Constants.CT_FHIR_JSON_NEW); 228 retVal.addFormat(Constants.FORMAT_JSON); 229 retVal.addFormat(Constants.FORMAT_XML); 230 retVal.setStatus(PublicationStatus.ACTIVE); 231 232 CapabilityStatementRestComponent rest = retVal.addRest(); 233 rest.setMode(RestfulCapabilityMode.SERVER); 234 235 Set<SystemRestfulInteraction> systemOps = new HashSet<>(); 236 Set<String> operationNames = new HashSet<>(); 237 238 Map<String, List<BaseMethodBinding<?>>> resourceToMethods = collectMethodBindings(theRequestDetails); 239 Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype = serverConfiguration.getNameToSharedSupertype(); 240 for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) { 241 242 if (nextEntry.getKey().isEmpty() == false) { 243 Set<TypeRestfulInteraction> resourceOps = new HashSet<>(); 244 CapabilityStatementRestResourceComponent resource = rest.addResource(); 245 String resourceName = nextEntry.getKey(); 246 247 RuntimeResourceDefinition def; 248 FhirContext context = serverConfiguration.getFhirContext(); 249 if (resourceNameToSharedSupertype.containsKey(resourceName)) { 250 def = context.getResourceDefinition(resourceNameToSharedSupertype.get(resourceName)); 251 } else { 252 def = context.getResourceDefinition(resourceName); 253 } 254 resource.getTypeElement().setValue(def.getName()); 255 resource.getProfile().setReference((def.getResourceProfile(serverBase))); 256 257 TreeSet<String> includes = new TreeSet<>(); 258 259 // Map<String, CapabilityStatement.RestResourceSearchParam> nameToSearchParam = new HashMap<String, 260 // CapabilityStatement.RestResourceSearchParam>(); 261 for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) { 262 if (nextMethodBinding.getRestOperationType() != null) { 263 String resOpCode = nextMethodBinding.getRestOperationType().getCode(); 264 if (resOpCode != null) { 265 TypeRestfulInteraction resOp; 266 try { 267 resOp = TypeRestfulInteraction.fromCode(resOpCode); 268 } catch (Exception e) { 269 resOp = null; 270 } 271 if (resOp != null) { 272 if (resourceOps.contains(resOp) == false) { 273 resourceOps.add(resOp); 274 resource.addInteraction().setCode(resOp); 275 } 276 if ("vread".equals(resOpCode)) { 277 // vread implies read 278 resOp = TypeRestfulInteraction.READ; 279 if (resourceOps.contains(resOp) == false) { 280 resourceOps.add(resOp); 281 resource.addInteraction().setCode(resOp); 282 } 283 } 284 285 if (nextMethodBinding.isSupportsConditional()) { 286 switch (resOp) { 287 case CREATE: 288 resource.setConditionalCreate(true); 289 break; 290 case DELETE: 291 if (nextMethodBinding.isSupportsConditionalMultiple()) { 292 resource.setConditionalDelete(ConditionalDeleteStatus.MULTIPLE); 293 } else { 294 resource.setConditionalDelete(ConditionalDeleteStatus.SINGLE); 295 } 296 break; 297 case UPDATE: 298 resource.setConditionalUpdate(true); 299 break; 300 default: 301 break; 302 } 303 } 304 } 305 } 306 } 307 308 checkBindingForSystemOps(rest, systemOps, nextMethodBinding); 309 310 if (nextMethodBinding instanceof SearchMethodBinding) { 311 SearchMethodBinding methodBinding = (SearchMethodBinding) nextMethodBinding; 312 if (methodBinding.getQueryName() != null) { 313 String queryName = bindings.getNamedSearchMethodBindingToName().get(methodBinding); 314 if (operationNames.add(queryName)) { 315 rest.addOperation().setName(methodBinding.getQueryName()).setDefinition(new Reference("OperationDefinition/" + queryName)); 316 } 317 } else { 318 handleNamelessSearchMethodBinding(rest, resource, resourceName, def, includes, (SearchMethodBinding) nextMethodBinding, theRequestDetails); 319 } 320 } else if (nextMethodBinding instanceof OperationMethodBinding) { 321 OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding; 322 String opName = bindings.getOperationBindingToId().get(methodBinding); 323 if (operationNames.add(opName)) { 324 // Only add each operation (by name) once 325 rest.addOperation().setName(methodBinding.getName().substring(1)).setDefinition(new Reference("OperationDefinition/" + opName)); 326 } 327 } 328 329 resource.getInteraction().sort(new Comparator<ResourceInteractionComponent>() { 330 @Override 331 public int compare(ResourceInteractionComponent theO1, ResourceInteractionComponent theO2) { 332 TypeRestfulInteraction o1 = theO1.getCode(); 333 TypeRestfulInteraction o2 = theO2.getCode(); 334 if (o1 == null && o2 == null) { 335 return 0; 336 } 337 if (o1 == null) { 338 return 1; 339 } 340 if (o2 == null) { 341 return -1; 342 } 343 return o1.ordinal() - o2.ordinal(); 344 } 345 }); 346 347 } 348 349 for (String nextInclude : includes) { 350 resource.addSearchInclude(nextInclude); 351 } 352 } else { 353 for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) { 354 checkBindingForSystemOps(rest, systemOps, nextMethodBinding); 355 if (nextMethodBinding instanceof OperationMethodBinding) { 356 OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding; 357 String opName = bindings.getOperationBindingToId().get(methodBinding); 358 if (operationNames.add(opName)) { 359 ourLog.debug("Found bound operation: {}", opName); 360 rest.addOperation().setName(methodBinding.getName().substring(1)).setDefinition(new Reference("OperationDefinition/" + opName)); 361 } 362 } 363 } 364 } 365 } 366 367 return retVal; 368 } 369 370 371 372 private void handleNamelessSearchMethodBinding(CapabilityStatementRestComponent rest, CapabilityStatementRestResourceComponent resource, String resourceName, RuntimeResourceDefinition def, TreeSet<String> includes, 373 SearchMethodBinding searchMethodBinding, RequestDetails theRequestDetails) { 374 includes.addAll(searchMethodBinding.getIncludes()); 375 376 List<IParameter> params = searchMethodBinding.getParameters(); 377 List<SearchParameter> searchParameters = new ArrayList<>(); 378 for (IParameter nextParameter : params) { 379 if ((nextParameter instanceof SearchParameter)) { 380 searchParameters.add((SearchParameter) nextParameter); 381 } 382 } 383 sortSearchParameters(searchParameters); 384 if (!searchParameters.isEmpty()) { 385 // boolean allOptional = searchParameters.get(0).isRequired() == false; 386 // 387 // OperationDefinition query = null; 388 // if (!allOptional) { 389 // RestOperation operation = rest.addOperation(); 390 // query = new OperationDefinition(); 391 // operation.setDefinition(new ResourceReferenceDt(query)); 392 // query.getDescriptionElement().setValue(searchMethodBinding.getDescription()); 393 // query.addUndeclaredExtension(false, ExtensionConstants.QUERY_RETURN_TYPE, new CodeDt(resourceName)); 394 // for (String nextInclude : searchMethodBinding.getIncludes()) { 395 // query.addUndeclaredExtension(false, ExtensionConstants.QUERY_ALLOWED_INCLUDE, new StringDt(nextInclude)); 396 // } 397 // } 398 399 for (SearchParameter nextParameter : searchParameters) { 400 401 String nextParamName = nextParameter.getName(); 402 403 String chain = null; 404 String nextParamUnchainedName = nextParamName; 405 if (nextParamName.contains(".")) { 406 chain = nextParamName.substring(nextParamName.indexOf('.') + 1); 407 nextParamUnchainedName = nextParamName.substring(0, nextParamName.indexOf('.')); 408 } 409 410 String nextParamDescription = nextParameter.getDescription(); 411 412 /* 413 * If the parameter has no description, default to the one from the resource 414 */ 415 if (StringUtils.isBlank(nextParamDescription)) { 416 RuntimeSearchParam paramDef = def.getSearchParam(nextParamUnchainedName); 417 if (paramDef != null) { 418 nextParamDescription = paramDef.getDescription(); 419 } 420 } 421 422 CapabilityStatementRestResourceSearchParamComponent param = resource.addSearchParam(); 423 param.setName(nextParamUnchainedName); 424 425// if (StringUtils.isNotBlank(chain)) { 426// param.addChain(chain); 427// } 428// 429// if (nextParameter.getParamType() == RestSearchParameterTypeEnum.REFERENCE) { 430// for (String nextWhitelist : new TreeSet<String>(nextParameter.getQualifierWhitelist())) { 431// if (nextWhitelist.startsWith(".")) { 432// param.addChain(nextWhitelist.substring(1)); 433// } 434// } 435// } 436 437 param.setDocumentation(nextParamDescription); 438 if (nextParameter.getParamType() != null) { 439 param.getTypeElement().setValueAsString(nextParameter.getParamType().getCode()); 440 } 441 for (Class<? extends IBaseResource> nextTarget : nextParameter.getDeclaredTypes()) { 442 RuntimeResourceDefinition targetDef = getServerConfiguration(theRequestDetails).getFhirContext().getResourceDefinition(nextTarget); 443 if (targetDef != null) { 444 ResourceType code; 445 try { 446 code = ResourceType.fromCode(targetDef.getName()); 447 } catch (FHIRException e) { 448 code = null; 449 } 450// if (code != null) { 451// param.addTarget(targetDef.getName()); 452// } 453 } 454 } 455 } 456 } 457 } 458 459 460 461 @Read(type = OperationDefinition.class) 462 public OperationDefinition readOperationDefinition(@IdParam IdType theId, RequestDetails theRequestDetails) { 463 if (theId == null || theId.hasIdPart() == false) { 464 throw new ResourceNotFoundException(Msg.code(628) + theId); 465 } 466 467 RestfulServerConfiguration serverConfiguration = getServerConfiguration(theRequestDetails); 468 Bindings bindings = serverConfiguration.provideBindings(); 469 470 List<OperationMethodBinding> operationBindings = bindings.getOperationIdToBindings().get(theId.getIdPart()); 471 if (operationBindings != null && !operationBindings.isEmpty()) { 472 return readOperationDefinitionForOperation(operationBindings); 473 } 474 List<SearchMethodBinding> searchBindings = bindings.getSearchNameToBindings().get(theId.getIdPart()); 475 if (searchBindings != null && !searchBindings.isEmpty()) { 476 return readOperationDefinitionForNamedSearch(searchBindings); 477 } 478 throw new ResourceNotFoundException(Msg.code(1985) + theId); 479 } 480 481 private OperationDefinition readOperationDefinitionForNamedSearch(List<SearchMethodBinding> bindings) { 482 OperationDefinition op = new OperationDefinition(); 483 op.setStatus(PublicationStatus.ACTIVE); 484 op.setKind(OperationKind.QUERY); 485 op.setIdempotent(true); 486 487 op.setSystem(false); 488 op.setType(false); 489 op.setInstance(false); 490 491 Set<String> inParams = new HashSet<>(); 492 493 for (SearchMethodBinding binding : bindings) { 494 if (isNotBlank(binding.getDescription())) { 495 op.setDescription(binding.getDescription()); 496 } 497 if (isBlank(binding.getResourceProviderResourceName())) { 498 op.setSystem(true); 499 } else { 500 op.setType(true); 501 op.addResourceElement().setValue(binding.getResourceProviderResourceName()); 502 } 503 op.setCode(binding.getQueryName()); 504 for (IParameter nextParamUntyped : binding.getParameters()) { 505 if (nextParamUntyped instanceof SearchParameter) { 506 SearchParameter nextParam = (SearchParameter) nextParamUntyped; 507 if (!inParams.add(nextParam.getName())) { 508 continue; 509 } 510 OperationDefinitionParameterComponent param = op.addParameter(); 511 param.setUse(OperationParameterUse.IN); 512 param.setType("string"); 513 param.getSearchTypeElement().setValueAsString(nextParam.getParamType().getCode()); 514 param.setMin(nextParam.isRequired() ? 1 : 0); 515 param.setMax("1"); 516 param.setName(nextParam.getName()); 517 } 518 } 519 520 if (isBlank(op.getName())) { 521 if (isNotBlank(op.getDescription())) { 522 op.setName(op.getDescription()); 523 } else { 524 op.setName(op.getCode()); 525 } 526 } 527 } 528 529 return op; 530 } 531 532 private OperationDefinition readOperationDefinitionForOperation(List<OperationMethodBinding> bindings) { 533 OperationDefinition op = new OperationDefinition(); 534 op.setStatus(PublicationStatus.ACTIVE); 535 op.setKind(OperationKind.OPERATION); 536 op.setIdempotent(true); 537 538 // We reset these to true below if we find a binding that can handle the level 539 op.setSystem(false); 540 op.setType(false); 541 op.setInstance(false); 542 543 Set<String> inParams = new HashSet<>(); 544 Set<String> outParams = new HashSet<>(); 545 546 for (OperationMethodBinding sharedDescription : bindings) { 547 if (isNotBlank(sharedDescription.getDescription())) { 548 op.setDescription(sharedDescription.getDescription()); 549 } 550 if (sharedDescription.isCanOperateAtInstanceLevel()) { 551 op.setInstance(true); 552 } 553 if (sharedDescription.isCanOperateAtServerLevel()) { 554 op.setSystem(true); 555 } 556 if (sharedDescription.isCanOperateAtTypeLevel()) { 557 op.setType(true); 558 } 559 if (!sharedDescription.isIdempotent()) { 560 op.setIdempotent(sharedDescription.isIdempotent()); 561 } 562 op.setCode(sharedDescription.getName().substring(1)); 563 if (sharedDescription.isCanOperateAtInstanceLevel()) { 564 op.setInstance(sharedDescription.isCanOperateAtInstanceLevel()); 565 } 566 if (sharedDescription.isCanOperateAtServerLevel()) { 567 op.setSystem(sharedDescription.isCanOperateAtServerLevel()); 568 } 569 if (isNotBlank(sharedDescription.getResourceName())) { 570 op.addResourceElement().setValue(sharedDescription.getResourceName()); 571 } 572 573 for (IParameter nextParamUntyped : sharedDescription.getParameters()) { 574 if (nextParamUntyped instanceof OperationParameter) { 575 OperationParameter nextParam = (OperationParameter) nextParamUntyped; 576 if (!inParams.add(nextParam.getName())) { 577 continue; 578 } 579 OperationDefinitionParameterComponent param = op.addParameter(); 580 param.setUse(OperationParameterUse.IN); 581 if (nextParam.getParamType() != null) { 582 param.setType(nextParam.getParamType()); 583 } 584 if (nextParam.getSearchParamType() != null) { 585 param.getSearchTypeElement().setValueAsString(nextParam.getSearchParamType()); 586 } 587 param.setMin(nextParam.getMin()); 588 param.setMax(nextParam.getMax() == -1 ? "*" : Integer.toString(nextParam.getMax())); 589 param.setName(nextParam.getName()); 590 } 591 } 592 593 for (ReturnType nextParam : sharedDescription.getReturnParams()) { 594 if (!outParams.add(nextParam.getName())) { 595 continue; 596 } 597 OperationDefinitionParameterComponent param = op.addParameter(); 598 param.setUse(OperationParameterUse.OUT); 599 if (nextParam.getType() != null) { 600 param.setType(nextParam.getType()); 601 } 602 param.setMin(nextParam.getMin()); 603 param.setMax(nextParam.getMax() == -1 ? "*" : Integer.toString(nextParam.getMax())); 604 param.setName(nextParam.getName()); 605 } 606 } 607 608 if (isBlank(op.getName())) { 609 if (isNotBlank(op.getDescription())) { 610 op.setName(op.getDescription()); 611 } else { 612 op.setName(op.getCode()); 613 } 614 } 615 616 if (op.hasSystem() == false) { 617 op.setSystem(false); 618 } 619 if (op.hasInstance() == false) { 620 op.setInstance(false); 621 } 622 623 return op; 624 } 625 626 /** 627 * Sets the cache property (default is true). If set to true, the same response will be returned for each invocation. 628 * <p> 629 * See the class documentation for an important note if you are extending this class 630 * </p> 631 * 632 * @deprecated Since 4.0.0 this doesn't do anything 633 */ 634 public ServerCapabilityStatementProvider setCache(boolean theCache) { 635 return this; 636 } 637 638 @Override 639 public void setRestfulServer(RestfulServer theRestfulServer) { 640 // ignore 641 } 642 643 private void sortSearchParameters(List<SearchParameter> searchParameters) { 644 Collections.sort(searchParameters, new Comparator<SearchParameter>() { 645 @Override 646 public int compare(SearchParameter theO1, SearchParameter theO2) { 647 if (theO1.isRequired() == theO2.isRequired()) { 648 return theO1.getName().compareTo(theO2.getName()); 649 } 650 if (theO1.isRequired()) { 651 return -1; 652 } 653 return 1; 654 } 655 }); 656 } 657}